How can I sum up a value from a loop of child components in react? - javascript

I have two react components one is Cart and the other is CartItem, Inside Cart I am looping using .map the CartItem. Inside CartItem I have a line total which is price * item.count. How can I sum up all the line totals regardless how many CartItems I have, and return the Total so that I can have the overall value of the cart.
I tried to add a handle total function but its only returning line total of one element not all of them.
Here is the CartItem:
const CartItem = ({item, handleTotal}) => {
var [itemCount, SetItemCount] = useState(1);
var [Linetotal, SetLineTotal] = useState(item.price);
const incrementcount = () => {
SetItemCount(++itemCount);
};
const decrementcount = () => {
SetItemCount(--itemCount);
};
useEffect(() => {
handleTotal(item.id,Linetotal)
},[])
return (
<div>
<Product>
<ProductDetail>
<Image src={item.image} />
<Details>
<ProductName>
<b>Product:</b> {item.name}
</ProductName>
<ProductId>
<b>ID:</b> {item.id}
</ProductId>
<ProductColor color={item.color} />
<ProductSize>
<b>Size:</b> {item.size}
</ProductSize>
</Details>
</ProductDetail>
<PriceDetail>
<ProductAmountContainer>
<Add onClick={() => {incrementcount(); SetLineTotal(item.price*itemCount)}} />
<ProductAmount>{itemCount}</ProductAmount>
<Remove onClick={() => {decrementcount(); SetLineTotal(item.price*itemCount)}} />
</ProductAmountContainer>
<ProductPrice>$ {item.price*itemCount}</ProductPrice>
</PriceDetail>
</Product>
</div>
);
};
export default CartItem;
and Here is the Cart:
const Cart = () => {
var [total, setTotal] = useState([]);
const handleTotal = (id,price) => {
setTotal(id,price);
};
return (
<Container>
<Navbar />
<Announcement />
<Wrapper>
<Title>YOUR BAG</Title>
<Top>
<TopButton>CONTINUE SHOPPING</TopButton>
<TopTexts>
<TopText>Shopping Bag(2)</TopText>
<TopText>Your Wishlist (0)</TopText>
</TopTexts>
<TopButton type="filled">CHECKOUT NOW</TopButton>
</Top>
<Bottom>
<Info>
{IntheCart.map((item) => (
<CartItem key={item.id} item={item} handleTotal={handleTotal} />
))}
<Hr />
</Info>
<Summary>
<SummaryTitle>ORDER SUMMARY</SummaryTitle>
<SummaryItem>
<SummaryItemText>Subtotal</SummaryItemText>
<SummaryItemPrice>$ 80</SummaryItemPrice>
</SummaryItem>
<SummaryItem>
<SummaryItemText>Estimated Shipping</SummaryItemText>
<SummaryItemPrice>$ 5.90</SummaryItemPrice>
</SummaryItem>
<SummaryItem>
<SummaryItemText>Shipping Discount</SummaryItemText>
<SummaryItemPrice>$ -5.90</SummaryItemPrice>
</SummaryItem>
<SummaryItem type="total">
<SummaryItemText>Total</SummaryItemText>
<SummaryItemPrice>$ 80</SummaryItemPrice>
</SummaryItem>
<Button>CHECKOUT NOW</Button>
</Summary>
</Bottom>
</Wrapper>
<Footer />
</Container>
);
};
export default Cart;

You can create updateTotal function in the Card component and once you increse or decrement item in the CardItem update the total value.
const CartItem = ({item, handleTotal, updateTotal}) => {
// code...
const incrementcount = () => {
SetItemCount(++itemCount);
updateTotal(item.price);
};
const decrementcount = () => {
SetItemCount(--itemCount);
updateTotal(-item.price);
};
// code...
updateTotal function.
var [totalPrice, setTotalPrice] = useState(0);
const updateTotal = (value) => {
setTotalPrice(prev => prev + value)
}
Add the updateTotal function to CardItem
<CartItem key={item.id} item={item} handleTotal={handleTotal} updateTotal={updateTotal} />

If you need sub sum to so sub total, you can do like this.
const Cart = () => {
...
var [subSum, setSubSum] = useState(null);
var [totalSum, setTotalSum] = useState(0);
const handleTotal = (id,price) => {
setSubSum({...subSum, id:price});
};
useEffect(()=>{
var sum = 0;
for (const [key, value] of Object.entries(subSum)) {
sum += value;
}
setTotalSum(sum);
},[total])
...
}

Related

How to add a new textfield on button click and add integer value of that input field to an array in react

I want to add a new input field on button click and add the integer value of that input field to an array in react
const [price, setPrice] = useState([])
const [count, setCount] = useState([1])
const addNewTextField = () => setCount(prev => [...prev,1])
const addInputValue= () => {
setPrice()
console.log(price)
}
<Button onClick={addNewTextField}>Add TextField</Button >
{
count.map((item, i) => {
return (
<TextField key={i} value={item.value} id={i} type='text' />
)
})
}
<Button onClick={addInputValue}>submit</Button >
first input value is 100,
second input value is 200,
result should be like this when I add new input field:
[100,200]
Try like below. You can keep only price state.
import { useState } from "react";
const App = () => {
const [price, setPrice] = useState([""]);
const addNewTextField = () => setPrice((prev) => [...prev, ""]);
const addInputValue = (i, newValue) => {
console.log(i, newValue);
setPrice((prevState) =>
prevState.map((value, valueIndex) =>
valueIndex === i ? newValue : value
)
);
};
console.log(price);
return (
<>
<button onClick={addNewTextField}>Add TextField</button>;
{price.map((item, i) => {
return (
<input
key={i}
placeholder={`input ${i}`}
// value={item}
id={i}
type="text"
onChange={(e) => addInputValue(i, e.target.value)}
/>
);
})}
<button onClick={addInputValue}>submit</button>
</>
);
};
export default App;
Code sandbox
const [price, setPrice] = useState([]);
const [count, setCount] = useState([1]);
const [value, setValue] = useState("");
const addNewTextField = () => setCount(prev => [...prev,prev + 1]);
const addInputValue= () => {
setPrice(price.concat(value));
console.log(price)
}
return(
<div>
<Button onClick={addNewTextField}>Add TextField</Button >
{
count.map((item, i) => {
return (
<TextField key={i} value={value} id={i} type='text'
onChange={e => setValue(e.target.value)} />
);
})
}
<Button onClick={addInputValue}>submit</Button >
</div>
);
Button and TextField components:
function Button(props){
return(
<button onClick={props.onClick}>{props.children}</button>
);
}
function TextField(props){
return(
<input type="text" id={props.id} value={props.value}
onChange={e=>props.onChange(e)}>
);
}

How can i get all image urls from my nasa api and add them into an image slider

This is my component of imageslider with button next and previous
I need som help how i can get individual img_src values and add them into another array and them use them in my image slider.
I welcome every solution corresponding to my aproach
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const { loading, error, Images } = ImageList;
useEffect(() => {
dispatch(ListImages());
}, [dispatch]);
var items = [Images.photos];
console.log(Images);
const classes = useStyles();
function Item(props) {
return (
<Paper>
{props.item.map(data => (
<img src={data.img_src} />
))}
{({ onClick, className, style, next, prev }) => {
return (
<Button onClick={onClick} className={classes.button} style={style}>
{next && "Next"}
{prev && "Previous"}
</Button>
);
}}
</Paper>
);
}
return (
<>
{loading ? (
<Loader />
) : error ? (
<h1>{error}</h1>
) : (
<Carousel>
{items.map((item, i) => (
<Item key={i} item={item} />
))}
</Carousel>
)}
</>
);
};
export default ImageSlider;
```
First of all you should move the Item component out of the ImageSlider. It is being redefined every render. You can use localState to keep track of the index.
const useImageIndexer = (maxIndex) => {
const [index, setIndex] = useState(0);
const nextImage = () => {
setIndex((current) => Math.min(maxIndex, current + 1));
};
const prevImage = () => {
setIndex((current) => Math.max(0, current - 1));
};
return [index, nextImage, prevImage];
}
Then to use inside the slider
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const photos = ImageList.Images.photos;
const [index, nextImage, prevImage] = useImageIndexer(photos.length);
const currentPhoto = photos[index];
// Further down in the code
if(loading) {
return (<Loader />);
}
if (error) {
return (<div>Oh no!</div>);
}
return (<div>
<img src={img.src} />
<button onClick={prevImage}>Previous</button>
<button onClick={nextImage}>Next</button>
</div>);
It seemed like you were wrapping photos which sounds like an array inside another array, that doesn't look right.
var items = [Images.photos];

How to call multiple instances of the same child component's function from parent

I know that using ref's I can call a function in a child component from the parent component. But my problem is I'm having multiple instances of the child component and I need to invoke the same function in all the children components.
// Get a hook function
const {useState, useRef, useImperativeHandle, useEffect} = React;
const Parent = ({title}) => {
const childRef1 = useRef();
const childRef2 = useRef();
const childRef3 = useRef();
const childRef4 = useRef();
const childRef5 = useRef();
const handleClick = () => {
childRef1.current.increment()
childRef2.current.increment()
childRef3.current.increment()
childRef4.current.increment()
childRef5.current.increment()
}
return (
<div>
<p>{title}</p>
<Child num={1} childRef={childRef1} />
<Child num={2} childRef={childRef2} />
<Child num={3} childRef={childRef3} />
<Child num={4} childRef={childRef4} />
<Child num={5} childRef={childRef5} />
<button onClick={() => handleClick()}>
Click me
</button>
</div>
);
};
const Child = ({num, childRef}) => {
const [count, setCount] = useState(0)
useImperativeHandle(childRef, () => ({
increment() {
// if(textEditable)
setCount(count+1)
},
}));
useEffect(() => {
setCount(num)
}, [num])
return (
<div>
{
count
}
</div>
)
}
// Render it
ReactDOM.render(
<Parent title="Example using Hooks:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
My question is how should I do the same if I'm rendering the child components inside a map function ?
I know that using ref's I can call a function...
Perfect! In that case, we will use the same procedure, but simply expand it a bit further.
Set either the component or the state to hold the child components...
this.state.children = [];
Then map() them like so...
var children = [1,2,3,4,5,6];
children.map((child) => {
return (
<Child
num={child}
ref={(instance) => {this.state.children[child] = instance}}
/>
);
});
Then call the functions on with a forEach() on them...
Object.keys(this.state.children).forEach((child) => {
var childinstance = this.state.children[child];
childinstance.specialFunction();
});
I did it with hooks like below.
// Get a hook function
const {useState, useRef, useImperativeHandle, useEffect} = React;
const Parent = ({title}) => {
const [count, setCount] = useState(0);
const [children, setChildren] = useState(5);
const [childRefs, setChildRefs] = useState([])
const handleClick = () => childRefs.forEach(c => c.current.increment())
useEffect(() => {
setChildRefs([...Array(children).keys()].map(e => React.createRef()))
}, [title])
return (
<div>
<p>{title}</p>
{
[...Array(children).keys()].map(e => <Child
num={e}
childRef={ childRefs[e] }
/>)
}
<button onClick={() => handleClick()}>
Click me
</button>
</div>
);
};
const Child = ({num, childRef}) => {
const [count, setCount] = useState(0)
useImperativeHandle(childRef, () => ({
increment() {
// if(textEditable)
setCount(count+1)
},
}));
useEffect(() => {
setCount(num)
}, [num])
return (
<div>
{
count
}
</div>
)
}
// Render it
ReactDOM.render(
<Parent title="Example using Hooks:" />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Track the state in the parent and pass it to the child as a prop:
const Parent = ({title}) => {
const [count, setCount] = useState(0);
return (
<div>
<p>{title}</p>
<Child count={count} />
<Child count={count} />
<Child count={count} />
</div>
)
}
If you need them to be independent, use an array for the state:
const Parent = ({title}) => {
const [count, setCount] = useState([0,1,42,96]);
const onIncrement = index => {
// probably a better way to do this. just here for illustrative purposes
const newCount = [...count];
newCount.splice(index, 1, count[index] + 1);
setCount(newCount);
}
const incrementAll = () => setCount(count.map(x => x + 1));
return (
<div>
<p>{title}</p>
{ count.map((entry, index) => (
<Child
num={entry}
key={index}
onIncrement={() => onIncrement(index))} {/* if you want the child to be able to do it */}
/>
)) }
<button onClick={incrementAll}>Click Me</button>
</div>
)
}

How to save the count on a counter app and present that count in a list showing previous counts made? (using react)

I have created a simple counter app that allows you to increment, decrement and reset the count. The next goal is to save the count, where you are able to show a history of previous calculations. Beginning with creating a function component for the 'Save Count' button.
I have gone about this by creating an empty useState array. Then creating an onClickFunction for saveCount, where the savedCount applies a spread operator to the count and then I map through the array every time the 'Save Count' button is pressed. This is based on a example of 'useState with Array' in the link below:
https://daveceddia.com/usestate-hook-examples/
You can find the code I have attempted below (also using this link: https://codesandbox.io/s/counter-app-project-save-nx7x6?file=/src/index.js:286-1297) :
function App() {
const [count, setCount] = useState(0);
const savedCount = useState([]);
const incrementCount = increment => {
setCount(count + increment);
};
const decrementCount = decrement => {
setCount(count - decrement);
};
const resetCount = () => {
setCount(0);
};
const saveCount = () => {
savedCount([
...count,
{
id: count.length
}
]);
resetCount();
};
return (
<div className="App">
<ButtonIncrement increment={1} onClickFunction={incrementCount} />
<ButtonIncrement increment={10} onClickFunction={incrementCount} />
<ButtonDecrement decrement={1} onClickFunction={decrementCount} />
<ButtonDecrement decrement={10} onClickFunction={decrementCount} />
<Reset onClickFunction={resetCount} />
<span>{count}</span>
<Save onClickFunction={saveCount} />
<ul>
{savedCount.map(count => (
<li key={count.id}>count</li>
))}
</ul>
</div>
);
}
function App() {
const [count, setCount] = useState(0);
const [savedCounts, setSavedCounts] = useState([]);
const incrementCount = increment => {
setCount(count + increment);
};
const decrementCount = decrement => {
setCount(count - decrement);
};
const resetCount = () => {
setCount(0);
};
const saveCount = () => {
setSavedCounts([...savedCounts, count]);
resetCount();
};
return (
<div className="App">
<ButtonIncrement increment={1} onClickFunction={incrementCount} />
<ButtonIncrement increment={10} onClickFunction={incrementCount} />
<ButtonDecrement decrement={1} onClickFunction={decrementCount} />
<ButtonDecrement decrement={10} onClickFunction={decrementCount} />
<Reset onClickFunction={resetCount} />
<span>{count}</span>
<Save onClickFunction={saveCount} />
<ul>
{savedCounts.map((savedCount, index) => (
<li key={index}>{savedCount}</li>
))}
</ul>
</div>
);
}
Fix savedCount as useState returns an array (tuple of [state,setState]).
Fix saveCount while using functional useState.
Render list items with unique key prop.
const [savedCount, setSavedCount] = useState([]);
const saveCount = () => {
setSavedCount(prev => [...prev, { id: count }]);
resetCount();
};
<ul>
{savedCount.map((count, index) => (
<li key={index}>count: {count.id}</li>
))}
</ul>

How to add a whole array to useState variable

I'm trying to add a whole array to useState variable
import React, { Fragment, useState, useEffect } from 'react';
import { Form, Button, Popover, OverlayTrigger } from 'react-bootstrap';
const Filter = props => {
const [formData, setFormData] = useState({
filter: ''
});
const [items, setItems] = useState([]);
const [retrievedItems, setRetrievedItems] = useState([]);
const addToFilter = newFilter => {
let retrievedFilter = ["da vinci","paris", "london"];
console.log(retrievedFilter);
if (retrievedFilter.length > 0) {
setRetrievedItems([...retrievedItems, retrievedFilter]);
retrievedFilter = 0;
setRetrievedItems([...retrievedItems, newFilter]);
} else {
setItems([...items, newFilter]);
}
console.log('items are: ', items);
console.log('retrieve filter', props.retrievedFilter);
console.log('retrieved items: ', retrievedItems);
};
useEffect(() => {
console.log('useEffect ', retrievedItems);
}, [retrievedItems]);
const deleteFilter = index => {
// props.retrievedFilter.splice(index, 1);
items.splice(index, 1);
setItems([...items]);
// setItems([...props.retrievedFilter, ...items]);
console.log(items);
};
const { filter } = formData;
const onChange = e => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const onSubmit = e => {
e.preventDefault();
addToFilter(filter);
// Passing filter data up (i.e: to components that use <Filter />)
props.filterData(filter);
//Close the Popover
document.body.click();
};
const popover = (
<Popover id="popover-basic">
<Form>
<Form.Group controlId="formGroupEmail">
<Form.Label>Add New Filter</Form.Label>
<Form.Control
type="text"
placeholder="New Filter"
name="filter"
onChange={e => onChange(e)}
/>
</Form.Group>
<Button variant="dark" type="submit" onClick={e => onSubmit(e)}>
Add
</Button>
</Form>
</Popover>
);
return (
<Fragment>
<label>
<p className="filter-title">{props.title}</p>
</label>
<div className={props.className ? props.className : 'filter'}>
{!props.retrievedFilter
? items.map((item, index) => {
return (
<div className="filter-text" key={index}>
{item}
<Button
className="filter-button"
size="sm"
onClick={() => deleteFilter(index)}
>
X
</Button>
</div>
);
})
: props.retrievedFilter.map((item, index) => {
return (
<div className="filter-text" key={index}>
{item}
<Button
className="filter-button"
size="sm"
onClick={() => deleteFilter(index)}
>
X
</Button>
</div>
);
})}
<OverlayTrigger
trigger="click"
placement="right"
rootClose
overlay={popover}
>
<p className="text-field">Type new one</p>
</OverlayTrigger>
</div>
</Fragment>
);
};
export default Filter;
however retrievedItems shows as an empty array in the console.
any help would be appreciated.
setState is async. You have to console.log inside an effect hook with the array as a parameter.
useEffect(() => console.log(retrieved_items), [ retrievedItems ])
The second parameter ensures that the effect fires in repose to a change in the values passed to it.
Per my comment, here is a code snippet that I think does what you want.
I couldn't get it running in SO but here's a codepen: https://codepen.io/anon/pen/PrYYmz?editors=1010 (watch the chrome console as you add items)
import React, {
Fragment,
useState,
useEffect
} from 'react';
const Filter = props => {
const [formData, setFormData] = useState({filter: ''});
const [items, setItems] = useState([]);
const [retrievedItems, setRetrievedItems] = useState([]);
const addToFilter = newFilter => {
let retrievedFilter = ["da vinci", "paris", "london"];
console.log('add', retrievedFilter);
if (retrievedFilter.length > 0) {
setRetrievedItems([...retrievedItems, retrievedFilter]);
retrievedFilter = 0;
setRetrievedItems([...retrievedItems, newFilter]);
} else {
setItems([...items, newFilter]);
}
console.log('items are: ', items);
console.log('retrieve filter', props.retrievedFilter);
console.log('retrieved items: ', retrievedItems);
};
useEffect(() => {
console.log('useEffect ', retrievedItems);
}, [retrievedItems]);
const deleteFilter = index => {
// props.retrievedFilter.splice(index, 1);
items.splice(index, 1);
setItems([...items]);
// setItems([...props.retrievedFilter, ...items]);
console.log(items);
};
const {filter} = formData;
const onChange = e => {
setFormData({ ...formData,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
addToFilter(filter);
// Passing filter data up (i.e: to components that use <Filter />)
//props.filterData(filter);
//Close the Popover
document.body.click();
};
return (
<Fragment >
<label >
<p className = "filter-title" > {
props.title
} </p> </label> <
div className = {
props.className ? props.className : 'filter'
} > {!props.retrievedFilter ?
items.map((item, index) => {
return ( <
div className = "filter-text"
key = {index} > {item} <button className = "filter-button" size = "sm" onClick = {() => deleteFilter(index)}>X</button></div>
);
}) :
props.retrievedFilter.map((item, index) => {
return ( <div className = "filter-text" key = {index} > {item} <button className = "filter-button" size = "sm" onClick = {() => deleteFilter(index)} >X</button></div>);})} <input type = "text" placeholder = "New Filter" name = "filter" onChange = {e => onChange(e) }/>
<button variant = "dark" type = "submit" onClick = {e => onSubmit(e)} >Add</button>
</div>
</Fragment>
);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>

Categories