在嵌套对象中,在React with Hooks中更新状态的正确方法是什么?

export Example = () => {
  const [exampleState, setExampleState] = useState(
  {masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b"
           fieldTwoTwo: "c"
           }
        }
   })

如何使用setExampleState将exampleState更新为a(附加字段)?

const a = {
masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }
}

b(改变值)?

const b = {masterField: {
        fieldOne: "e",
        fieldTwo: {
           fieldTwoOne: "f"
           fieldTwoTwo: "g"
           }
        }
   })

当前回答

我给您留下了一个实用函数来不可变地更新对象

/**
 * Inmutable update object
 * @param  {Object} oldObject     Object to update
 * @param  {Object} updatedValues Object with new values
 * @return {Object}               New Object with updated values
 */
export const updateObject = (oldObject, updatedValues) => {
  return {
    ...oldObject,
    ...updatedValues
  };
};

你可以这样用

const MyComponent = props => {

  const [orderForm, setOrderForm] = useState({
    specialities: {
      elementType: "select",
      elementConfig: {
        options: [],
        label: "Specialities"
      },
      touched: false
    }
  });


// I want to update the options list, to fill a select element

  // ---------- Update with fetched elements ---------- //

  const updateSpecialitiesData = data => {
    // Inmutably update elementConfig object. i.e label field is not modified
    const updatedOptions = updateObject(
      orderForm[formElementKey]["elementConfig"],
      {
        options: data
      }
    );
    // Inmutably update the relevant element.
    const updatedFormElement = updateObject(orderForm[formElementKey], {
      touched: true,
      elementConfig: updatedOptions
    });
    // Inmutably update the relevant element in the state.
    const orderFormUpdated = updateObject(orderForm, {
      [formElementKey]: updatedFormElement
    });
    setOrderForm(orderFormUpdated);
  };

  useEffect(() => {
      // some code to fetch data
      updateSpecialitiesData.current("specialities",fetchedData);
  }, [updateSpecialitiesData]);

// More component code
}

如果没有,您可以在这里找到更多实用程序:https://es.reactjs.org/docs/update.html

其他回答

可以使用useReducer钩子来管理复杂的状态,而不是useState。要做到这一点,首先初始化状态和更新函数如下所示:

const initialState = { name: "Bob", occupation: "builder" };
const [state, updateState] = useReducer(
  (state, updates) => ({ ...state, ...updates }),
  initialState
);

然后你可以通过只传递部分更新来更新状态,就像这样:

updateState({ occupation: "postman" })
function App() {

  const [todos, setTodos] = useState([
    { id: 1, title: "Selectus aut autem", completed: false },
    { id: 2, title: "Luis ut nam facilis et officia qui", completed: false },
    { id: 3, title: "Fugiat veniam minus", completed: false },
    { id: 4, title: "Aet porro tempora", completed: true },
    { id: 5, title: "Laboriosam mollitia et enim quasi", completed: false }
  ]);

  const changeInput = (e) => {todos.map(items => items.id === parseInt(e.target.value) && (items.completed = e.target.checked));
 setTodos([...todos], todos);}
  return (
    <div className="container">
      {todos.map(items => {
        return (
          <div key={items.id}>
            <label>
<input type="checkbox" 
onChange={changeInput} 
value={items.id} 
checked={items.completed} />&nbsp; {items.title}</label>
          </div>
        )
      })}
    </div>
  );
}

如果有人正在搜索useState()钩子,则更新为对象

- Through Input

        const [state, setState] = useState({ fName: "", lName: "" });
        const handleChange = e => {
            const { name, value } = e.target;
            setState(prevState => ({
                ...prevState,
                [name]: value
            }));
        };

        <input
            value={state.fName}
            type="text"
            onChange={handleChange}
            name="fName"
        />
        <input
            value={state.lName}
            type="text"
            onChange={handleChange}
            name="lName"
        />
   ***************************

 - Through onSubmit or button click
    
        setState(prevState => ({
            ...prevState,
            fName: 'your updated value here'
         }));

一般来说,你应该注意React状态下嵌套很深的对象。为了避免意外的行为,状态应该不可更改地更新。当你有深层对象时,你最终会为了不变性而对它们进行深层克隆,这在React中是相当昂贵的。为什么?

一旦你深度克隆状态,React将重新计算和重新渲染所有依赖于变量的东西,即使它们没有改变!

因此,在尝试解决问题之前,首先考虑如何将状态变平。一旦您这样做了,您就会发现有助于处理大型状态的方便工具,例如useReducer()。

如果你想过,但仍然确信你需要使用深度嵌套的状态树,你仍然可以使用useState()与immutable.js和Immutability-helper等库一起使用。它们使得更新或克隆深层对象变得简单,而不必担心可变性。

我已经给出了两个追加,整个对象更新,具体的关键更新的解决方案的例子

追加和修改都可以通过一个简单的步骤来完成。我认为这是更稳定和安全的,没有不可变或可变的依赖。

这就是追加新对象的方法

setExampleState(prevState => ({
    ...prevState,
    masterField2: {
        fieldOne: "c",
        fieldTwo: {
            fieldTwoOne: "d",
            fieldTwoTwo: "e"
        }
    },
}))

假设您想再次修改masterField2对象。可能有两种情况。您想要更新整个对象或更新对象的特定键。

更新整个对象-这里masterField2键的整个值将被更新。

setExampleState(prevState => ({
    ...prevState,
    masterField2: {
        fieldOne: "c",
        fieldTwo: {
            fieldTwoOne: "d",
            fieldTwoTwo: "e"
        }
    },
}))

但是如果你只想改变masterField2对象中的fieldTwoOne键呢?你可以这样做。

let oldMasterField2 = exampleState.masterField2
oldMasterField2.fieldTwo.fieldTwoOne = 'changed';
setExampleState(prevState => ({
    ...prevState,
    masterField2: oldMasterField2
}))