应用 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
中,同步代码会合并渲染,异步代码不会合并渲染。
上面的代码只会渲染一次,它会将setLoading
和setData
进行合并。这个其实和类组件是一样的,在异步函数中不会合并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...