3

Use useState to render multiple times

When using hooks , I often write the following code, and then you will find that the page has been rendered twice, which sometimes causes more headaches.

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

In React , synchronous code will merge rendering, and asynchronous code will not merge rendering.

The following code will only render once, it will setLoading and setData . This is actually the same as the class component, setState will not be merged in the asynchronous function.

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

hooks to solve multiple rendering problems in class components, but it is more troublesome in 061bb5b8c4e9f5.

Method 1: Combine multiple states into one state

Put all the dependent states into one object and setState together in 061bb5b8c4ea1b to solve the problem of multiple renderings, as shown in the following code

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

But there is a problem. If you only want setState , you need to pass in other dependencies as well, otherwise this value will be lost. React internally will not help you to merge.

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

The solution is to use the spread operator

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

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

Method 2: Write a custom merged dependency hook

Every time setState has to use the extended operation conformity and dependency is too troublesome.

Use React customize the hook function and write a useMergeState hook that merges dependencies.

Custom hook needs to start 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
}, []);

Option 3: Use useReducer

React provides useReducer to manage various dependencies instead of using 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
}, []);

If you want to get the last state, you need to modify the above code.

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
}, []);

Reference article: https://stackoverflow.com/questions/53574614/multiple-calls-to-state-updater-from-usestate-in-component-causes-multiple-re-re


uccs
756 声望88 粉丝

3年 gis 开发,wx:ttxbg210604