1

使用useState多次渲染问题

 2 years ago
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.
neoserver,ios ssh client

使用 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...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK