如何使用 useState 更新初始状态对象的特定 属性

How to update a specific property of an initial state object using useState

考虑一下我需要在获取一些数据后更新状态的代码,我只需要更新初始状态的数据部分 (clientData):

 const [clientData, setClientData] = useState({
    data: {},
    product: 'profile'
 });

 useEffect(() => {
    getProducts(1).then(res =>
      setClientData({ ...clientData, data: res.data })
    );
  }, []);

我怎样才能并且是否有可能只更新初始状态的 "data" 部分而不必传播它 (...clientData)

这是不可能的,因为从 useState 挂钩 replaces the prior state value 返回的 set 方法与您传递给它的所有内容都是完整的。

一种可能性是使用多个挂钩将您的状态分开,这将为您提供更新组件状态的更精细的方法:

/* Separate your state across multiple hooks */
const [data, setData] = useState({});
const [product, setProduct] = useState('profile');

useEffect(() => {
    getProducts(1).then(res =>
      /* Only update data portion of state via setData hook */
      setData(res.data)
    );
 }, []);

这里有几个选项...

1) 分开以下...

const [clientData, setClientData] = useState({
    data: {},
    product: 'profile'
 });

进入...

const [clientData, setClientData] = useState({});
const [clientProduct, setClientProduct] = useState('profile');

然后您可以更新您需要的数据部分。在您上面列出的情况下,可能建议这样做。或者...

2) 使用像 'immer' 这样的库,它允许您通过简单地修改现有的状态树来创建新的状态树...

import produce from 'immer';

...

const [clientData, setClientData] = useState({
  data: {},
  product: 'profile'
});

useEffect(() => {
  getProducts(1).then(res =>
    setClientData(produce(clientData, draft => {
      draft.data = res.data;
    }));
  );
}, []);

Immer 是一个很棒的库,您可以了解更多关于它的信息here

这对于 useState 是不可能的。不过 useReducer 是可能的。

如前所述,最佳做法是在多个挂钩之间拆分此状态,但是,实际上可以使用 useState() 使用这样的函数来做到这一点:

const setClientDataSelectively = ({data=null, product=null}) => {
    setClientData((prevState) => ({
        data: data !== null ? data : data,
        product: product !== null ? product : product,
    }));
}

参数默认为null,然后使用prevState对象和一个conditional operator,你可以检查是否有东西实际上是传入并相应地属性。