我可以使用多个useState和useReducer而没有副作用吗?

时间:2020-07-31 07:22:46

标签: reactjs

我有三个问题:

  1. 我可以接着使用多个useState而没有副作用吗?我知道可以将这两个值放在一个对象中,但是如果我不想这样做,是否有可能没有副作用?像它会重新触发两个渲染吗?:

     useEffect(
       () => {
         setCount(count);
         setText(`Clicked ${count}`);
       },
       [count],
     );
    
  2. 与上述相同的问题,但是将useState与useReducer交换:

      useEffect(
        () => {
          setCount({ type: 'onClick', count });
          setText({ type: 'onChangeText', text: `Clicked ${count}` });
        },
        [count],
      );
    
  3. 同样是一个问题,但每个问题中只有一个:

     useEffect(
        () => {
          setCount(count);
          setText({ type: 'onChangeText', text: `Clicked ${count}` });
        },
        [count],
      );
    

2 个答案:

答案 0 :(得分:0)

您的所有示例仅呈现一次,因为您没有在异步函数后设置状态或调度。如果要在异步函数之后执行设置状态或调度,则组件将在会导致状态更改的每个设置状态或调度上呈现。

尽管您的示例有点差劲,因为当count更改为count的值时,为什么要在效果中设置count?就像这样做:setCount(count=>count),并且不进行任何设置也不会导致重新渲染。

您可以使用ReactDOM.unstable_batchedUpdates批量更新多个状态或调度多个动作,但是如果这些值相关,那么也许您应该将它们组合为状态或创建一个可同时更新两个值的reducer。

这里是一个演示,展示了在设置状态或在异步函数后多次调度时的多个渲染。使用stable_batchedUpdates时,没有多个渲染。

const reducer = (state, action) => state + 1;
const useABC = () => {
  const [a, setA] = React.useState(0);
  const [b, setB] = React.useState(0);
  const [c, setC] = React.useState(0);
  const [state, dispatch] = React.useReducer(reducer, 0);

  const ref = React.useRef([]);
  const reducerRef = React.useRef([]);
  ref.current = ref.current.concat(
    `render:${ref.current.length + 1} a:${a} b:${b} c:${c}`
  );
  reducerRef.current = reducerRef.current.concat(
    `render:${
      reducerRef.current.length + 1
    } state:${state}, c:${c}`
  );
  const up = React.useCallback(
    () => setC((c) => c + 1),
    []
  );
  return [
    [setA, setB, up, c, ref],
    [state, dispatch, reducerRef],
  ];
};
const Output = React.memo(function Output({ up, c, r }) {
  return (
    <div>
      <button onClick={up}>{c}</button>
      <pre>{JSON.stringify(r, undefined, 2)}</pre>
    </div>
  );
});
//when c increases it causes one render
//  then when a is set to c it causes a render
//  then when b is set to c it causes a render
const SetState = () => {
  const [[setA, setB, up, c, ref]] = useABC();
  React.useEffect(() => {
    Promise.resolve().then(() => {
      setA(c);
      setB(c);
    });
  }, [c, setA, setB]);
  return <Output c={c} up={up} r={ref.current} />;
};
//when c increases it causes one render
//  then when state is increased it causes a render
//  then when state is increased again it causes a render
const Reducer = () => {
  const [[, , up, c], [, dispatch, ref]] = useABC();
  React.useEffect(() => {
    if (c !== 0) {
      Promise.resolve().then(() => {
        dispatch();
        dispatch();
      });
    }
  }, [c, dispatch]);
  return <Output c={c} up={up} r={ref.current} />;
};
const BatchUpdated = () => {
  const [[setA, setB, up, c, ref]] = useABC();
  React.useEffect(() => {
    Promise.resolve().then(() => {
      //using batch updated
      ReactDOM.unstable_batchedUpdates(() => {
        setA(c);
        setB(c);
      });
    });
  }, [c, setA, setB]);
  return <Output c={c} up={up} r={ref.current} />;
};

const App = () => {
  return (
    <div>
      <h1>batch updates</h1>
      <BatchUpdated />
      <h1>set state</h1>
      <SetState />
      <h1>reducer</h1>
      <Reducer />
    </div>
  );
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 1 :(得分:0)

希望您知道在依赖它的效果中更新状态时发生了什么

这是代码沙箱,请思考为什么它显示i=11 enter image description here https://codesandbox.io/s/optimistic-ride-4rvci?file=/src/App.js

如果您了解我的观点,则可以自由使用它,并进行无限循环去皮

这不是一个好习惯,如果您不需要循环,只需更新其他状态或在jsx中使用countcount中的tmp vars即可;如果需要循环,为什么不显式循环并在计算后更新最终状态