使用useState多次渲染问题
source link: https://segmentfault.com/a/1190000041132302
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
使用 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 }, []);
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK