I am now trying to make table with checkboxes, and got really irritating problem. I reproduced this problem in this example.
I have list of items of format {id, value}. For each item I create div element with checkbox and text inside. When I want to make item selected, I add item id into selectedItems list.
The problem is that I can't make component checkbox update its state on selectedItems list change.
Tell me please, how to do it normally? In Vue, I could just create computed property for each sub-component and pass selectedItems and binded prop. How to do it in React?
import {Checkbox} from "@mui/material";
import {useMemo, useState} from "react";
export default function Example() {
const items = useState([{id: 0, value: "Item 1"}, {id: 1, value: "Item 2"}, {id: 2, value: "Item 3"}])
const [selectedItems, setSelectedItems] = useState([])
console.log(items)
function ListItems({items}) {
return useMemo(() => {
const elements = []
for (let i = 0; i < items[0].length; i ) {
const item = items[0][i]
const checked = selectedItems.indexOf(item.id) !== -1
console.log(item, items.length)
elements.push(
<div key={i}>
<Checkbox
checked={checked}
onChange={() => {
let state = selectedItems
if (checked) {
state = state.filter(id => id !== item.id)
} else {
state.push(item.id)
}
setSelectedItems(state)
}}
/>
<p>{item.value}</p>
</div>
)
}
return elements
}, [items])
}
return (
<div style={{display: "flex", flexDirection: "column"}}>
<ListItems items={items}/>
</div>
)
}
Codesandbox: https://codesandbox.io/s/not-working-computed-props-react-forked-fywtxp?file=/src/App.js
CodePudding user response:
Apart from my comment about selectedItems
that should be provided in useMemo
dependencies, you have other issue, which is that you don't modify your state really here setSelectedItems(state)
Arrays are being compared by reference, so if you have const x = y = []
; and then x.push('1')
, x
is still equal to y
. Hence when you do
let state = selectedItems
...
state.push(...)
...
setSelectedItems(state)
It doesn't trigger re-render cause state
is still equals to previous one (to selectedItems
)
The fix could be either
state = [...state, item.id]
instead of push
Or setSelectedItems([...state])