Реагировать на хуки Exhaustive-deps async infinite Loop

У меня есть следующий компонент:

import React, { useState, useEffect } from "react";

const App = () => {
    const [data, setData] = useState<null | any[]>(null);
    const [checked, setChecked] = useState(false);
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        setLoading(true);

        (async () => {
            if (data) {
                // Could do something else here if data already exsisted
                console.log("Data exists already");
            }

            const ret = await fetch("https://jsonplaceholder.typicode.com/users?delay=1000", { cache: "no-store" });
            const json = await ret.json();
            setData(json);
            setLoading(false);
        })();

    }, [checked]);

    return (
        <>
            <h1>useEffect Testing {loading && " ⌛"}</h1>
            <hr />
            <input type="checkbox" checked={checked} onChange={(e) => setChecked(e.target.checked)} />
            <pre style={{ fontSize: "0.8em" }}>{JSON.stringify(data)}</pre>
        </>
    );
};

export default App;

В настоящее время мой if(data) однако бессмысленно, если бы я хотел проверить текущий dataсостояние, а затем запустить асинхронную функцию на основе состояния, в котором eslint (react-hooks / excustive-deps) сообщает мне, что в хуке отсутствуетdataзависимость, которая есть. Проблема возникает, когда я добавляю данные в список зависимостей, вызывая бесконечный цикл.

Есть идеи, как это решить? Кажется, что это должен быть довольно простой шаблон, однако все, что я нахожу, рекомендуется с использованием расширенногоsetState(prev => prev + 1) перегрузка, которая мне в этом случае не помогает.

1 ответ

Решение

Вы устанавливаете dataа также читая это в эффекте. Это вызовет бесконечный цикл, если вы не остановите его вручную. Вот несколько решений.

Вернуть, если есть данные, вместо их изменения:

useEffect(() => {
    setLoading(true);

    (async () => {
        if (data) {
            // Could do something else here if data already exsisted
            console.log("Data exists already");
            return;
        }

        const ret = await fetch("https://jsonplaceholder.typicode.com/users?delay=1000", { cache: "no-store" });
        const json = await ret.json();
        setData(json);
        setLoading(false);
    })();

}, [checked, data]);

Избавьтесь от зависимости от data в эффекте, который его устанавливает (что бы я сделал):

useEffect(() => {
    setLoading(true);

    (async () => {
        const ret = await fetch("https://jsonplaceholder.typicode.com/users?delay=1000", { cache: "no-store" });
        const json = await ret.json();
        setData(json);
        setLoading(false);
    })();

}, [checked]);

useEffect(() => {
        if (data) {
            // Could do something else here if data already exsisted
            console.log("Data changed and exists!");
        }
}, [data]);

Или вы можете вручную сделать что-то, что останавливает бесконечный цикл.

useEffect(() => {
    if (loading || data) {
      console.log('Do something with loading or data, but do not modify it!')
      return;
    }

    setLoading(true);

    (async () => {
        const ret = await fetch("https://jsonplaceholder.typicode.com/users?delay=1000", { cache: "no-store" });
        const json = await ret.json();
        setData(json);
        setLoading(false);
    })();
}, [checked, data]);
Другие вопросы по тегам