乐趣区

关于前端:使用useState多次渲染问题

应用 useState 屡次渲染问题

应用 hooks 时常常会写出上面的代码,而后就会发现页面渲染了两遍,有时候会更头疼。

const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(async () => {const res = await axios.get("xxx");
  setLoading(false);
  setData(res);
}, []);

React 中,同步代码会合并渲染,异步代码不会合并渲染。

上面的代码只会渲染一次,它会将 setLoadingsetData进行合并。这个其实和类组件是一样的,在异步函数中不会合并setState

const [loading, setLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() => {setLoading(false);
  setData({a: 1});
}, []);

类组件中解决屡次渲染问题比拟好弄,然而在 hooks 就比拟麻烦。

办法一:将多个状态合并到一个状态中

将所有的依赖状态都放到一个对象中,在 setState 一起设置,就能解决屡次渲染的问题了,如下代码

const [request, setRequest] = useState({loading: true, data: null});
useEffect(async () => {const res = await axios.get("xxx");
  setRequest({loading: false, data: res});
}, []);

然而这样有个问题,如果只想 setState 一个依赖项时,须要将别的依赖项也要传进去,否则这个值会失落。React外部并不会帮你做去合并。

setRequest({data: res}); // loading 值失落了。

解决办法是应用扩大运算符

setRequest({...request, data: res});

// 或者
setRequest((prevState) => ({...prevState, data: res}));

办法二:写一个自定义合并依赖项的hook

每次 setState 都要应用扩大运算符合并依赖项太麻烦了。

应用 React 自定义 hook 性能,写一个合并依赖项的 useMergeState 钩子。

自定义 hook 须要应用 use 结尾。

const useMergeState = (initialState) => {const [state, setState] = useState(initialState);
  const setMergeState = (newState) =>
    setState((prevState) => ({...prevState, newState}));
  return [state, setMergeState];
};

/* 应用 */
const [request, setRequest] = useMegeState({loading: false, data: null});
useEffect(async () => {const res = await axios.get("xxx");
  setRequest({loading: true, data: res});

  // ...

  setRequest({data: { a: 1} }); // loading 状态不会失落,还是 true
}, []);

计划三:应用useReducer

React提供了 useReducer 来治理各个依赖项,而不是应用useState

const [request, setRequest] = useReducer((prevState, newState) => ({...prevState, newState}),
  {loading: false, data: null}
);

useEffect(async () => {const res = await axios.get("xxx");
  setRequest({loading: true, data: res});

  // ...

  setRequest({data: { a: 1} }); // loading 状态不会失落,还是 true
}, []);

如果想要获取上一个状态,须要对下面的代码进行革新。

const [request, setRequest] = useReducer((prevState, newState) => {const newWithPrevState = typeof newState === "function" ? newState(prevState) : newState;
    return{...prevState, newWithPrevState})
    },
  {loading: false, data: null}
);

useEffect(async () => {const res = await axios.get("xxx");
  setRequest((prevState) => {return { loading: true, data: res}
  });

  // ...

  setRequest({data: { a: 1} }); // loading 状态不会失落,还是 true
}, []);

参考文章:https://stackoverflow.com/que…

退出移动版