在嵌套对象中,在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"
           }
        }
   })

当前回答

答案已经有了,但是这种类型没有被提到,所以看看这种类型的例子…

 const[data,setdata]= useState({
    username: [
      email,
      "required", 
      //...some additional codes
    ],
    password: [
      password,
      "required|password-5",
     //..additional code if any..
    ],
  })

**要在输入字段中更新状态变量email,您可以添加类似的代码与您的变量名**

          <Input
              onChangeText={(t) => setdata(prevState=>({...prevState,username:{[0]:t}}))}
              value={data.username[0]}
            />

其他回答

你可以像这样传递新值:

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
   })

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

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

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

updateState({ occupation: "postman" })

最初我在useState中使用object,但后来我移动到useReducer钩子用于复杂的情况。重构代码时,我感到性能有所提高。

当您有涉及多个子值的复杂状态逻辑时,或者当下一个状态依赖于前一个状态时,useReducer通常比useState更可取。

useReducer React文档

我已经实现了这样的钩子供我自己使用:

/**
 * Same as useObjectState but uses useReducer instead of useState
 *  (better performance for complex cases)
 * @param {*} PropsWithDefaultValues object with all needed props 
 * and their initial value
 * @returns [state, setProp] state - the state object, setProp - dispatch 
 * changes one (given prop name & prop value) or multiple props (given an 
 * object { prop: value, ...}) in object state
 */
export function useObjectReducer(PropsWithDefaultValues) {
  const [state, dispatch] = useReducer(reducer, PropsWithDefaultValues);

  //newFieldsVal={[field_name]: [field_value], ...}
  function reducer(state, newFieldsVal) {
    return { ...state, ...newFieldsVal };
  }

  return [
    state,
    (newFieldsVal, newVal) => {
      if (typeof newVal !== "undefined") {
        const tmp = {};
        tmp[newFieldsVal] = newVal;
        dispatch(tmp);
      } else {
        dispatch(newFieldsVal);
      }
    },
  ];
}

更多相关的钩子。

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

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

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

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

如果你使用布尔值和数组,这可以帮助你:

const [checkedOrders, setCheckedOrders] = useState<Record<string, TEntity>>({});

const handleToggleCheck = (entity: TEntity) => {
  const _checkedOrders = { ...checkedOrders };
  const isChecked = entity.id in checkedOrders;

  if (isChecked) {
    delete _checkedOrders[entity.id];
  } else {
    _checkedOrders[entity.id] = entity;
  }

  setCheckedOrders(_checkedOrders);
};