Reputation: 3051
I am trying to change its value when you click on a checkbox. But for some reason the changes are not rendered again. why?
sample data (props.data):
{
"school": {
"checkDocuments": [
{
'label': 'license',
'check': false
},
{
'label': 'identification',
'check': false
},
{
'label': 'phone',
'check': false
},
],
"section": "escuela"
},
"collage": {
"checkDocuments": [
{
'label': 'license',
'check': false
}
],
"section": "universidad"
}
this is my component:
export const Verificardocuments = props => {
const [documents, setDocuments] = useState(props.data);
updateDataDocument = (section, index) => {
console.log(section, index);
documents[section].checkDocuments[index].check =
!documents[section].checkDocuments[index].check;
setDocuments(documents);
}
return
<List>
{
/* escuela,universidad => "section" key */
Object.keys(documents).map((section, i) => {
return (
<View key={i}>
<ListItem itemDivider >
<Text>{documents[section].section}</Text>
</ListItem>
{documents[section].checkDocuments.map((document, j) => {
return (
<ListItem onPress={() => updateDataDocument(section, j)} key={j} >
<CheckBox checked={document.check} color="blue" />
<Body>
<Text>{document.label}</Text>
</Body>
</ListItem>)
})}
</View>
)
})
}
</List>
}
I am not sure if I am doing the best way to update an element contained in my array.
Upvotes: 0
Views: 277
Reputation: 11790
State updates in function components must be immutable.
It is recommended to use the callback pattern when updating objects inside the state and spread each object to avoid losing the rest of the properties
updateDataDocument = (section, index) => {
setDocuments(prevState => ({
// spread the previous state into a new object
...prevState,
[section]: {
// also spread prevState[section] into a new object
...prevState[section],
// create a new array of checkDocuments
checkDocuments: prevState[section].checkDocuments.map((doc, i) =>
i === index
? // when we find the object we need to update,
// spread it into a new object and then update
// the check property
{ ...doc, check: !doc.check }
: doc
),
},
}))
}
Nested immutable updates are ugly and it might be better to introduce an immutable library that will handle it for you such as immer which have a hooks version which lets you update the state in a mutable way
import { useImmer } from 'use-immer'
export const Verificardocuments = props => {
const [documents, setDocuments] = useImmer(props.data);
const updateDataDocument = (section, index) => {
setDocuments(draft => {
const prevChecked = draft[section].checkDocuments[index].check
draft[section].checkDocuments[index].check = !prevChecked
})
}
return ...
}
Upvotes: 1
Reputation: 59511
Here's one way to do it.
Since your data structure is somewhat complex/big, you need to create a copy for every nested object or array within. Once that is done, you can safely update the copy without also changing the original variable (the state). Then, with the copy being updated to how you want the new state to be, you simply use it to set the new state.
updateDataDocument = (section, index) => {
const obj = {...documents}; // Copy the documents object
const arr = [...obj[section].checkDocuments] // Copy the checkDocuments array
const item = {...arr[index]} // Copy the item inside the array
item.check = !item.check; // Change the value
arr[index] = item;
obj[section].checkDocuments = arr;
setDocuments(obj);
}
Another way, which effectively is the same thing, is how @AsafAviv did it.
Upvotes: 1
Reputation: 474
documents
is being mutated that's why it doesn't trigger re-render. so i advise to assign it to a local copy and do the mutation there
updateDataDocument = (section, index) => {
const newDoc = documents
newDoc[section].checkDocuments[index].check = !newDoc[section].checkDocuments[index].check;
setDocuments(newDoc)
}
Upvotes: -1