useEffect 和 useCallback 导致多次重新渲染

时间:2021-03-03 12:43:15

标签: javascript reactjs use-effect usecallback

我正在处理我的一个小项目,就在部署之前,我使用的 API 进行了策略更改以将 API 调用限制为每秒一次。这导致我出现错误(错误 429:请求过多)。 我的解决方法(因为 axios 没有 retry after header prop 是使用 fetch API 创建某种递归函数,如果调用失败,它会再次调用)。所以,最初我最终得到了下面的代码块:

//recursive function to make an api call
    const fetchCalls = useCallback((url, setState, retries = 4) => {
        fetch(url)
            .then((res) => {
                // check if successful. If so, return the response transformed to json
                if (res.ok) {
                    return res.json();
                }
                // else, return a call to fetchRetry if number of retries >0
                if (retries > 0) {
                    return fetchCalls(url, setState, retries - 1);
                } else {
                    throw new Error(res);
                }
            })
            .then((data) => {
                if (data !== undefined) {
                    setState(data);
                    Axios.all([
                        Axios.get(
                            `${api.sparklineBase}${setLowerCase(
                                data[0].name
                            )}/market_chart/range?vs_currency=${
                                userData.currency.code
                            }&from=${dayUNIX}&to=${currentUNIX}`
                        ),
                        Axios.get(
                            `${api.sparklineBase}${setLowerCase(
                                data[0].name
                            )}/market_chart/range?vs_currency=${
                                userData.currency.code
                            }&from=${yearUNIX}&to=${currentUNIX}`
                        ),
                    ])
                        .then((ress) => {
                            setSparkline((prevState) => {
                                return [...prevState, ress[0].data];
                            });
                            setSparkline((prevState) => {
                                return [...prevState, ress[1].data];
                            });
                        })
                        .catch((errr) => {
                            console.log(errr);
                        });
                }
                // Do something with the response
            })
            .catch((error) => {
                console.log(error);
            });
    }, [userData, setSparkline]);

    useEffect(() => {
        Axios.get(`${api.zoneBase}apiKey=${api.zoneKey}&include=useragent`)
            .then((response) => {
                setUserData(response.data);
//calling fetchCalls function below
                fetchCalls( `${api.base}key=${api.key}&ids=${match.params.id}&convert=${response.data.currency.code}&interval=1d,7d,30d,365d`,
                    setCryptos
                );      
            })
            .catch((err) => {
                console.log(err);
            });

        return () => {
            setCryptos([]);
            setSparkline([]);
        };
    }, [setCryptos, setSparkline, setUserData, match, fetchCalls, cryptos]);

显然它导致了如此多的重新渲染猜测导致 useEffect 和 useCallback 中的一些依赖项是实际的异步状态值。

决定将 fetchCall(没有 useCallback)扔到 useEffect 中,我最终得到了这个:

//forgot to comment out this bit.
const fetchCalls = useCallback((url, setState, retries = 4) => {
        fetch(url)
            .then((res) => {
                // check if successful. If so, return the response transformed to json
                if (res.ok) {
                    return res.json();
                }
                // else, return a call to fetchRetry
                if (retries > 0) {
                    return fetchCalls(url, setState, retries - 1);
                } else {
                    throw new Error(res);
                }
            })
            .then((data) => {
                if (data !== undefined) {
                    setState(data);
                }
                // Do something with the response
            })
            .catch((error) => {
                console.log(error);
            });
    }, []);

    useEffect(() => {
        Axios.get(`${api.zoneBase}apiKey=${api.zoneKey}&include=useragent`)
            .then((response) => {
                setUserData(response.data);

                const fetchCalls = (url, setState, retries = 4) => {
                    fetch(url)
                        .then((res) => {
                            // check if successful. If so, return the response transformed to json
                            if (res.ok) {
                                return res.json();
                            }
                            // else, return a call to fetchRetry
                            if (retries > 0) {
                                return fetchCalls(url, setState, retries - 1);
                            } else {
                                throw new Error(res);
                            }
                        })
                        .then((data) => {
                            if (data !== undefined) {
                                setState(data);
                                Axios.all([
                                    Axios.get(
                                        `${api.sparklineBase}${setLowerCase(
                                            data[0].name
                                        )}/market_chart/range?vs_currency=${
                                            response.data.currency.code
                                        }&from=${dayUNIX}&to=${currentUNIX}`
                                    ),
                                    Axios.get(
                                        `${api.sparklineBase}${setLowerCase(
                                            data[0].name
                                        )}/market_chart/range?vs_currency=${
                                            response.data.currency.code
                                        }&from=${weekUNIX}&to=${currentUNIX}`
                                    ),
                                ])
                                    .then((ress) => {
                                        setSparkline((prevState) => {
                                            return [...prevState, ress[0].data];
                                        });
                                        setSparkline((prevState) => {
                                            return [...prevState, ress[1].data];
                                        });
                                    })
                                    .catch((errr) => {
                                        console.log(errr);
                                    });
                            }
                            // Do something with the response
                        })
                        .catch((error) => {
                            console.log(error);
                        });
                };

                fetchCalls(
                    `${api.base}key=${api.key}&ids=${match.params.id}&convert=${response.data.currency.code}&interval=1d,7d,30d,365d`,
                    setCryptos
                );
            })
            .catch((err) => {
                console.log(err);
            });

        return () => {
            setCryptos([]);
            setSparkline([]);
        };
    }, [setCryptos, setSparkline, setUserData, match, fetchCalls]);

上面的代码按预期工作。然而,我不知道我忘记注释掉原始的 fetchCalls 函数(在 useEffect 之外),因为我只注释掉了依赖于 API 调用 fetchCalls 函数的第二组 API 调用。现在尝试将其注释掉会导致错误,指出未定义 fetchCalls 并且 fetchCalls 是不必要的依赖项。

但是请注意,在第二个代码块中,useEffect 中的 fetchCalls 承载了第二组 API 调用,这些 API 调用依赖于 useEffect 中的 fetchCalls 函数应该进行的 API 调用。

现在我只想知道为什么它会这样工作,因为我觉得这一切都很奇怪。另外,我可以使用更可接受的解决方法吗?

0 个答案:

没有答案