What I am trying to do: I need price * quantity = total. I have a table with price as one , quantity as one and total as one . Quantity is a counter and needs to have its own state as rows will be added with more counters. But I need that value in the parent table so I can use it to calculate total.
Normally I would put the counter value in the parent, but if I do that, when I change one counter they all change. So how else can I pass the value back up to the parent? Or what other option do I have here.
//parent
const memoData = useMemo(() => {
return productList.map((product) => (
<tr key={product.productNumber} className='productTable__row'>
<td>
<input
placeholder='Product name'
className='productTable__input productTable__input_product'
defaultValue={product.productName}
/>
</td>
<td>
<input
placeholder='Id'
className='productTable__input productTable__input_id'
disabled
defaultValue={product.productNumber}
/>
</td>
<td>
<input
placeholder='Price'
className='productTable__input productTable__input_price'
defaultValue={product.price}
/>
</td>
<td>
<Input prouct={product} />
</td>
<td>
<input
placeholder='Total'
className='productTable__input productTable__input_price'
defaultValue={? * product.price}
/>
</td>
<td>
<button
className='productTable__btn_close'
onClick={() => onProductDelete(product)}
>
<RiCloseFill fill='red' size='30px' />
</button>
</td>
</tr>
));
}, [onProductDelete, currentProduct, productList]);
//child
function Input() {
const [counter, setCounter] = useState(1);
const addOneClick = () => {
setCounter(counter + 1);
};
const minusOneClick = () => {
if (counter > 0) {
setCounter(counter - 1);
}
};
return (
<div style={{ width: '88px' }}>
<button
className='productTable__btn_minus'
style={{ borderRight: 'none' }}
onClick={minusOneClick}
>
<FaMinus fill='var(--color-primary-blue)' size='14px' />
</button>
<input
placeholder='0'
className='productTable__input productTable__input_quantity'
// defaultValue={0}
value={counter}
/>
<button
className='productTable__btn_plus'
style={{ borderLeft: 'none' }}
onClick={addOneClick}
>
<FaPlus fill='var(--color-primary-blue)' size='14px' />
</button>
</div>
);
}
You should consider productList
as "the state" in the parent, instead of counter
, and you should have counter
as a property of product
:
// productList
[
{
id: 123,
counter: 0, // <-- add a counter property
productNumber: '...',
// ...
},
]
// parent
return productList.map( product => (
<tr key={ product.productNumber } >
<td>
{/* add onSetCounter() callback */}
<Input
product={ product }
onSetCounter={ id => { setCounter( id, count ); } }
/>
</td>
</tr>
));
// child
function Input() {
const counter = props.product.counter; // <-- use props
const setCounter = props.setCounter; // <-- use props
// const [counter, setCounter] = useState(1); // <-- don't use local state
const addOneClick = () => {
setCounter(counter + 1);
};
return (
<button onClick={ addOneClick } >add one</button>
);
}
// update the counter
const setCounter = function( id, count ){
// ... I don't know how you are managing your productList.
// I would put this code into my redux-reducer. ...
const newProductList = productList.reduce( (acc, product) => {
if( product.id === id ){ // current product
acc.push({
...product,
counter: count
})
} else { // other products
acc.push( product );
};
}, []);
};
(I wrote props.product
here, despite you are passing <Input prouct={...} />
, I assume that was a typing mistake)