useState vs useReducer

时间:2019-02-12 09:19:14

标签: reactjs react-hooks

  当您具有涉及多个子值的复杂状态逻辑时,或者当下一个状态依赖于前一个状态时,

useReducer通常比useState更可取。 useReducer还可让您优化触发深度更新的组件的性能,因为您可以传递调度而不是回调。

(引用https://reactjs.org/docs/hooks-reference.html#usereducer

我对粗体部分感兴趣,该部分指出在上下文中使用useReducer而不是useState

我尝试了两种变体,但它们似乎没有什么不同。

我比较这两种方法的方式如下:

const [state, updateState] = useState();
const [reducerState, dispatch] = useReducer(myReducerFunction);

我将它们每个都传递给一个上下文对象,该上下文对象在一个更深的孩子中使用(我只是运行了单独的测试,将值替换为要测试的函数)。

<ContextObject.Provider value={updateState // dispatch}>

孩子包含这些功能

const updateFunction = useContext(ContextObject);
useEffect(
  () => {
    console.log('effect triggered');
    console.log(updateFunction);
  },
  [updateFunction]
);

在这两种情况下,当父对象重新渲染时(由于另一个局部状态更改),效果永远不会运行,这表明更新功能在渲染之间未更改。 我读引号中的粗体字是否错了?还是我忽略了什么?

2 个答案:

答案 0 :(得分:3)

  

useReducer还可让您优化组件的性能,   触发深度更新,因为您可以传递调度而不是传递   回调。

上面的语句并不是要表明useState返回的setter是在每次更新或渲染时新创建的。意思是,当您有复杂的逻辑来更新状态时,您根本不会直接使用setter来更新状态,而是您将编写一个复杂的函数,该函数会依次调用具有更新状态的setter之类的

const handleStateChange = () => {
   // lots of logic to derive updated state
   updateState(newState);
}

ContextObject.Provider value={{state, handleStateChange}}>

在上述情况下,每次重新渲染父对象时,都会创建一个新的handleStateChange实例,从而使Context Consumer也重新渲染。

上述情况的一种解决方案是使用useCallback并记住状态更新程序方法并使用它。但是,为此,您需要解决与使用方法中的值相关的关闭问题。

因此,建议使用useReducer,该方法返回在重新渲染之间不变的dispatch方法,并且可以在化简器中使用操作逻辑。

答案 1 :(得分:0)

何时需要关心它

如果您在render上创建一个回调并将其传递给子组件,则该子组件的属性将更改。但是,当父级渲染时,常规组件将重新渲染(到虚拟dom),甚至道具也保持不变。例外是实现了shouldComponentUpdate并比较属性(例如PureComponent)的classComponent。

这是一种优化,只有在重新渲染子组件需要大量计算时(如果多次在同一屏幕上渲染它,或者需要进行深度渲染或大量重新渲染),您才应该关心它。

如果是这种情况,则应确保:  1.您的孩子是扩展PureComponent的类组件  2.避免将新创建的函数作为道具传递。而是通过     分派,从React.useState返回的setter或备忘录     定制的二传手。

使用记忆化的自定义设置器

尽管我不建议为特定组件构建唯一的备注设置器(需要注意一些事项),但是您可以使用通用挂钩来为您实现。

这里是useObjState钩子的示例,该钩子提供了简单的API,并且不会引起其他折旧。


const useObjState = initialObj => {
  const [obj, setObj] = React.useState(initialObj);
  const memoizedSetObj = React.useMemo(() => {
    const helper = {};
    Object.keys(initialObj).forEach(key => {
      helper[key] = newVal =>
        setObj(prevObj => ({ ...prevObj, [key]: newVal }));
    });
    return helper;
  }, []);
  return [obj, memoizedSetObj];
};

function App() {
  const [user, memoizedSetUser] = useObjState({
    id: 1,
    name: "ed",
    age: null,
  });

  return (
      <NameComp
        setter={memoizedSetUser.name}
        name={user.name}
      />
  );
}

const NameComp = ({name, setter}) => (
  <div>
    <h1>{name}</h1>
      <input
        value={name}
        onChange={e => setter(e.target.value)}
      />
  </div>
)

Demo