React: Setting State for Deeply Nested Objects w/ Hooks

独自空忆成欢 提交于 2021-01-20 19:41:09

问题


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 inside that nested object, I have to use a hook to set the state. I can't seem to get at the deeper nested items, though. I have a drop down menu w/ an onChange handler. . .inside the onChange handler is an inline function to directly setValue of whatever key/val pair is changing.

The syntax I'm using after the spread operator in each inline function is wrong, however.

As a workaround, I have resorted to factoring out the inline function to its own function that rewrites the entire state object every time the state changes, but that is extremely time consuming and ugly. I'd rather do it inline like the below:

 const [stateObject, setStateObject] = useState({

    top_level_prop: [
      {
        nestedProp1: "nestVal1",
        nestedProp2: "nestVal2"
        nestedProp3: "nestVal3",
        nestedProp4: [
          {
            deepNestProp1: "deepNestedVal1",
            deepNestProp2: "deepNestedVal2"
          }
        ]
      }
    ]
  });

<h3>Top Level Prop</h3>

   <span>NestedProp1:</span>
     <select
       id="nested-prop1-selector"
       value={stateObject.top_level_prop[0].nestedProp1}
       onChange={e => setStateObject({...stateObject, 
       top_level_prop[0].nestedProp1: e.target.value})}
     >
      <option value="nestVal1">nestVal1</option>
      <option value="nestVal2">nestVal2</option>
      <option value="nestVal3">nestVal3</option>
     </select>

<h3>Nested Prop 4</h3>

   <span>Deep Nest Prop 1:</span>
     <select
       id="deep-nested-prop-1-selector"
       value={stateObject.top_level_prop[0].nestprop4[0].deepNestProp1}
       onChange={e => setStateObject({...stateObject, 
       top_level_prop[0].nestedProp4[0].deepNestProp1: e.target.value})}
     >
      <option value="deepNestVal1">deepNestVal1</option>
      <option value="deepNestVal2">deepNestVal2</option>
      <option value="deepNestVal3">deepNestVal3</option>
     </select>

The result of the code above gives me a "nestProp1" and "deepNestProp1" are undefined, presumably because they are never being reached/having their state changed by each selector. My expected output would be the selected option matching the value of whatever the selector's current val is (after the state changes). Any help would be greatly appreciated.


回答1:


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"/>



回答2:


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"/>


来源:https://stackoverflow.com/questions/57798841/react-setting-state-for-deeply-nested-objects-w-hooks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!