I\'m working with a deeply nested state object in React. My code base dictates that we try to stick with function components and so every time I want to update a key/value pair
I think you should be using the functional form of setState
, so you can have access to the current state and update it.
Like:
setState((prevState) =>
//DO WHATEVER WITH THE CURRENT STATE AND RETURN A NEW ONE
return newState;
);
See if that helps:
function App() {
const [nestedState,setNestedState] = React.useState({
top_level_prop: [
{
nestedProp1: "nestVal1",
nestedProp2: "nestVal2",
nestedProp3: "nestVal3",
nestedProp4: [
{
deepNestProp1: "deepNestedVal1",
deepNestProp2: "deepNestedVal2"
}
]
}
]
});
return(
<React.Fragment>
<div>This is my nestedState:</div>
<div>{JSON.stringify(nestedState)}</div>
<button
onClick={() => setNestedState((prevState) => {
prevState.top_level_prop[0].nestedProp4[0].deepNestProp1 = 'XXX';
return({
...prevState
})
}
)}
>
Click to change nestedProp4[0].deepNestProp1
</button>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
UPDATE: With dropdown
function App() {
const [nestedState,setNestedState] = React.useState({
propA: 'foo1',
propB: 'bar'
});
function changeSelect(event) {
const newValue = event.target.value;
setNestedState((prevState) => {
return({
...prevState,
propA: newValue
});
});
}
return(
<React.Fragment>
<div>My nested state:</div>
<div>{JSON.stringify(nestedState)}</div>
<select
value={nestedState.propA}
onChange={changeSelect}
>
<option value='foo1'>foo1</option>
<option value='foo2'>foo2</option>
<option value='foo3'>foo3</option>
</select>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Another approach is to use the useReducer hook
const App = () => {
const reducer = (state, action) =>{
return {...state, [action.type]: action.payload}
}
const [state, dispatch] = React.useReducer(reducer,{
propA: 'foo1',
propB: 'bar1'
});
const changeSelect = (prop, event) => {
const newValue = event.target.value;
dispatch({type: prop, payload: newValue});
}
return(
<React.Fragment>
<div>My nested state:</div>
<div>{JSON.stringify(state)}</div>
<select
value={state.propA}
onChange={(e) => changeSelect('propA', e)}
>
<option value='foo1'>foo1</option>
<option value='foo2'>foo2</option>
<option value='foo3'>foo3</option>
</select>
<select
value={state.propB}
onChange={(e) => changeSelect('propB', e)}
>
<option value='bar1'>bar1</option>
<option value='bar2'>bar2</option>
<option value='bar3'>bar3</option>
</select>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>