I am trying to update my state. I want to be able to update a specific isChecked property when it is updated when a user clicks a checkbox. My state below:
this.state = {
userPermissions: [
{
appName: "Dashboard",
roles: [
{
appId: "1",
statusId: 1,
isChecked: false
},
{
appId: "1",
statusId: 2,
isChecked: true
},
]
},
{
appName: "Finance",
roles: [
{
appId: "2",
statusId: 3,
isChecked: false
},
{
appId: "2",
statusId: 4,
isChecked: true
},
]
}
]
}
This is what I have tried. I have to map twice to get to the roles and then set the conditions, however, I am assuming I am mutating the state with another new one so when I render it, it breaks.
handleCheck = (e, data) => {
const checked = e.target.checked;
this.setState(prevState => ({
userPermissions: prevState.userPermissions
.map(item => item.roles
.map(item => item.appId === data.appId && item.statusId === data.statusId ? { ...item, isChecked: checked } : item))
}))
}
Is there a cleaner way to do this without mutation? Thanks.
let state = {
keys: ['Finance', 'Dashboard'],
Finance: [
{
appId: '2',
statusId: 3,
isChecked: false,
},
{
appId: '2',
statusId: 4,
isChecked: true,
},
],
Dashboard: [
{
appId: '1',
statusId: 1,
isChecked: false,
},
{
appId: '1',
statusId: 2,
isChecked: true,
},
],
};
this.setState(prev => {
let Finance = prev.Finance.map(item =>
item.appId === data.appId && item.statusId === data.statusId
? { ...item, isChecked: checked }
: item
);
let Dashboard = prev.Dashboard.map(item =>
item.appId === data.appId && item.statusId === data.statusId
? { ...item, isChecked: checked }
: item
);
return {
keys: prev.keys,
Finance,
Dashboard,
};
});
I have changed your state structure,I think it is more readable and you can read the state object and debug it much better
your object changes to this after you run it through your maps 
this.setState((state) => {
const { userPermissions } = state;
const index = userPermissions.findIndex(e => e.roles.findIndex(ee => ee.appId === "1")!= -1)
const roleIndex = userPermissions[index].roles.findIndex(role => role.appId === "1")
return {
...state,
userPermissions: [
...userPermissions.slice(0, index),
Object.assign(
{ ...userPermissions.slice[index] },
{
roles: [
...userPermissions[index].roles.slice(0, roleIndex),
Object.assign(userPermissions[index].roles[roleIndex], {
isChecked: !userPermissions[index].roles[roleIndex].isChecked
}),
...userPermissions[index].roles.slice(roleIndex + 1)
]
}
),
...userPermissions.slice(index + 1)
]
};
});
If anyone has issues with this in the future, I have posted the way I have solved this. I feel this is a cleaner solution.
const { userPermissions } = this.state;
const { checked } = e.target;
//find the app index
const appIndex = userPermissions.findIndex(element => element.appName === data.appName);
//find the status index
const roleIndex = userPermissions[appIndex].roles.findIndex(role => role.statusId=== data.statusId);
//create a copy of state
let newArr = [...userPermissions];
//make change on isChecked
newArr[appIndex].roles[roleIndex] = { ...newArr[appIndex].roles[roleIndex], isChecked: checked };
this.setState({
userPermissions: newArr
})