Wie kann ich eine fehlende Abhängigkeitswarnung bei Verwendung von useEffect React Hook beheben?

172

Mit React 16.8.6 (es war in der vorherigen Version 16.8.3 gut) erhalte ich diesen Fehler, wenn ich versuche, eine Endlosschleife bei einer Abrufanforderung zu verhindern

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Ich konnte keine Lösung finden, die die Endlosschleife stoppt. Ich möchte mich von der Verwendung fernhalten useReducer(). Ich habe diese Diskussion unter https://github.com/facebook/react/issues/14920 gefunden, bei der eine mögliche Lösung darin besteht, dass You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.ich nicht sicher bin, was ich tue. Daher habe ich noch nicht versucht, sie zu implementieren.

Ich habe dieses aktuelle Setup. React hook useEffect läuft ununterbrochen für immer / Endlosschleife und der einzige Kommentar ist, mit useCallback()dem ich nicht vertraut bin.

Wie ich aktuell benutze useEffect()(was ich am Anfang nur einmal ausführen möchte, ähnlich wie componentDidMount())

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
russ
quelle

Antworten:

187

Wenn Sie die fetchBusinesses-Methode nur außerhalb des Effekts verwenden, können Sie sie einfach in den Effekt verschieben und die Warnung vermeiden

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

Wenn Sie jedoch fetchBusinesses außerhalb von Render verwenden, müssen Sie zwei Dinge beachten

  1. Gibt es eine Frage mit Ihnen nicht vorbei fetchBusinessesals Methode , wenn es während der Montage mit seinem einschließenden Verschluss verwendet wird ?
  2. Hängt Ihre Methode von einigen Variablen ab, die sie von ihrem umschließenden Abschluss erhält? Dies ist bei Ihnen nicht der Fall.
  3. Bei jedem Rendern werden fetchBusinesses neu erstellt, und daher führt die Übergabe an useEffect zu Problemen. Sie müssen sich also zuerst fetchBusinesses merken, wenn Sie es an das Abhängigkeitsarray übergeben möchten.

Zusammenfassend würde ich sagen, dass Sie die Regel deaktivieren können , wenn Sie fetchBusinessesaußerhalb von useEffectverwenden, // eslint-disable-next-line react-hooks/exhaustive-depsandernfalls können Sie die Methode innerhalb von useEffect verschieben

Um die Regel zu deaktivieren, würden Sie sie wie folgt schreiben

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
Shubham Khatri
quelle
14
Ich habe die von Ihnen beschriebene Lösung verwendet. Eine andere Lösung, die ich aufgrund eines anderen Setups an anderer Stelle verwendet habe, war useCallback(). Also zum Beispiel: const fetchBusinesses= useCallback(() => { ... }, [...]) und das useEffect()würde so aussehen:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
russ
1
@russ, Sie sind richtig, Sie müssten fetchBusiness mit useCallback auswendig lernen, wenn Sie es an das Abhängigkeitsarray übergeben
möchten
Es wäre schön, wenn Sie zeigen würden, wo die eslint-disable-Anweisung abgelegt werden soll. Ich dachte, es wäre über useEffect
user210757
1
Es // eslint-disable-next-line react-hooks/exhaustive-depsist wie ein Hack, dem Linter zu erklären, dass Ihr Code korrekt ist. Ich
gehe davon aus,
1
@ TapasAdhikary, ja, Sie können eine asynchrone Funktion in useEffect haben, Sie müssen sie nur anders schreiben. Bitte überprüfen Sie stackoverflow.com/questions/53332321/…
Shubham Khatri
75
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Es ist kein JS / React-Fehler, sondern eine Eslint-Warnung (Eslint-Plugin-React-Hooks).

Es sagt Ihnen, dass der Hook von der Funktion abhängt fetchBusinesses, also sollten Sie ihn als Abhängigkeit übergeben.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

Es kann dazu führen, dass bei jedem Rendern eine Funktion aufgerufen wird, wenn die Funktion in einer Komponente wie folgt deklariert ist:

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

weil jede Zeitfunktion mit neuer Referenz neu deklariert wird

Die richtige Vorgehensweise ist:

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

oder einfach nur die Funktion in definieren useEffect

Mehr: https://github.com/facebook/react/issues/14920

Fard
quelle
13
Dies führt zu einem neuen FehlerLine 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
russ
1
Die Lösung ist in Ordnung, und wenn Sie für die Funktion einen anderen Status ändern, müssen Sie die Abhängigkeiten hinzufügen, um ein weiteres unerwartetes Verhalten zu vermeiden
cesarlarsson
53

Sie können es direkt als useEffectRückruf festlegen :

useEffect(fetchBusinesses, [])

Es wird nur einmal ausgelöst. Stellen Sie daher sicher, dass alle Abhängigkeiten der Funktion korrekt eingestellt sind (wie bei Verwendung von componentDidMount/componentWillMount...).


Bearbeiten 21.02.2020

Nur der Vollständigkeit halber:

1. Verwenden Sie die Funktion als useEffectRückruf (wie oben).

useEffect(fetchBusinesses, [])

2. Deklarieren Sie die Funktion im Inneren useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Merken Sie sich mit useCallback()

In diesem Fall müssen Sie Abhängigkeiten in Ihre Funktion aufnehmen, wenn Sie Abhängigkeiten in Ihrer Funktion haben. useCallbackDies wird erneut ausgelöst useEffect, wenn sich die Parameter der Funktion ändern. Außerdem ist es eine Menge Boilerplate ... Also einfach die Funktion direkt an useEffectwie in übergeben 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Deaktivieren Sie die Warnung von eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
jpenna
quelle
2
Ich liebe dich ... Diese Antwort ist so vollständig!
Nick09
8

Die Lösung wird auch durch Reagieren gegeben. Sie empfehlen Ihnen, useCallbackeine Memoize-Version Ihrer Funktion zurückzugeben:

Mit der Funktion 'fetchBusinesses' ändern sich die Abhängigkeiten von useEffect Hook (in Zeile NN) bei jedem Rendern. Um dies zu beheben, verpacken Sie die Definition 'fetchBusinesses' in eine eigene useCallback () Hook-React-Hooks / erschöpfende-Deps

useCallbackist einfach zu verwenden, da es dieselbe Signatur hat, da useEffectder Unterschied darin besteht, dass useCallback eine Funktion zurückgibt. Es würde so aussehen:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
Stephane L.
quelle
1

Dieser Artikel ist eine gute Einführung in das Abrufen von Daten mit Hooks: https://www.robinwieruch.de/react-hooks-fetch-data/

Fügen Sie im Wesentlichen die Definition der Abruffunktion hinzu useEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);
halloitsjoe
quelle
1

Sie können das Array vom 2. Argumenttyp entfernen [], das fetchBusinesses()wird jedoch auch bei jedem Update aufgerufen. Sie können IFder fetchBusinesses()Implementierung eine Anweisung hinzufügen , wenn Sie möchten.

React.useEffect(() => {
  fetchBusinesses();
});

Die andere besteht darin, die fetchBusinesses()Funktion außerhalb Ihrer Komponente zu implementieren . Vergessen Sie nur nicht fetchBusinesses(dependency), gegebenenfalls Abhängigkeitsargumente an Ihren Aufruf zu übergeben.

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}
5ervant
quelle
0

Eigentlich sind die Warnungen sehr nützlich, wenn Sie mit Hooks entwickeln. aber in einigen Fällen kann es Sie nadeln. vor allem, wenn Sie nicht auf Abhängigkeitsänderungen warten müssen.

Wenn Sie fetchBusinessesdie Abhängigkeiten des Hooks nicht einfügen möchten , können Sie sie einfach als Argument an den Rückruf des Hooks übergeben und den Hauptwert fetchBusinesseswie folgt als Standardwert festlegen

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

Dies ist keine bewährte Methode , kann jedoch in einigen Fällen hilfreich sein.

Wie Shubnam schrieb, können Sie den folgenden Code hinzufügen, um ESLint anzuweisen, die Überprüfung auf Ihren Hook zu ignorieren.

// eslint-disable-next-line react-hooks/exhaustive-deps
Behnam Azimi
quelle
0

Ich möchte [ fetchBusinesses] am Anfang nur einmal ausführen, ähnlich wiecomponentDidMount()

Sie können fetchBusinessesvollständig aus Ihrer Komponente herausziehen :

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

Dies bietet nicht nur eine einfache Lösung und löst die erschöpfende Warnung. fetchBusinessJetzt kann es besser getestet werden und erleichtert sich Comp, da es sich im Modulbereich außerhalb des Reaktionsbaums befindet.

Der Umzug nach fetchBusinessesdraußen funktioniert hier gut, da wir aufgrund des veralteten Verschlussbereichs ( dep in ) ohnehin nur die ersten Requisiten und den Status der Komponente lesen können .[]useEffect

So lassen Sie Funktionsabhängigkeiten aus

  • Funktion innerhalb des Effekts verschieben
  • Funktion außerhalb der Komponente verschieben - (wir verwenden diese)
  • Funktion beim Rendern aufrufen und useEffectvon diesem Wert abhängen lassen (reine Berechnungsfunktion)
  • Fügen Sie die Funktion hinzu, um Deps zu bewirken, und schließen Sie sie useCallbackals letzten Ausweg ein

In Bezug auf andere Lösungen:

Das Ziehen nach fetchBusinessesinnen useEffect()hilft nicht wirklich, wenn Sie auf einen anderen Status zugreifen. eslint würde sich immer noch beschweren: Codesandbox .

Ich würde mich auch davor zurückhalten, erschöpfende Kommentare zu ignorieren. Es ist einfach zu leicht, sie zu vergessen, wenn Sie Ihre Abhängigkeiten umgestalten und überarbeiten.

ford04
quelle
0
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

Diese Lösung ist ziemlich einfach und Sie müssen es-lint-Warnungen nicht überschreiben. Behalten Sie einfach ein Flag bei, um zu überprüfen, ob die Komponente bereitgestellt ist oder nicht.

Yasin
quelle
0

du versuchst es so

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

und

useEffect(() => {
    fetchBusinesses();
  });

Es ist Arbeit für dich. Aber mein Vorschlag ist, auf diese Weise zu versuchen, auch für Sie zu arbeiten. Es ist besser als vorher. Ich benutze diesen Weg:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

Wenn Sie Daten auf der Basis einer bestimmten ID erhalten, fügen Sie im Rückruf useEffect hinzu, [id]und Sie können keine Warnung anzeigen React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

Kashif
quelle
-4

Deaktivieren Sie einfach eslint für die nächste Zeile.

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

Auf diese Weise verwenden Sie es genau wie eine Komponente (einmal aufgerufen).

Aktualisiert

oder

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

fetchBusinesses wird jedes Mal aufgerufen, wenn sich someDeps ändern

user3550446
quelle
Anstatt zu deaktivieren, tun Sie einfach Folgendes: [fetchBusinesses]Die Warnung wird automatisch entfernt, und das Problem wurde für mich behoben.
Rotimi-Best
7
@ RotimiBest - dies führt zu einem unendlichen erneuten Rendern, wie in der Frage beschrieben
user210757
Ich habe es vor einiger Zeit in einem meiner Projekte so gemacht und es hat keine Endlosschleife erzeugt. Ich werde es aber noch einmal überprüfen.
Rotimi-Best
@ user210757 Warten Sie, aber warum wird eine Endlosschleife verursacht? Es ist nicht so, als würden Sie den Status nach dem Abrufen der Daten vom Server festlegen. Wenn Sie den Status aktualisiert haben, schreiben Sie einfach eine if-Bedingung, bevor Sie die Funktion aufrufen, die useEffectprüft, ob der Status leer ist.
Rotimi-Best
@ rotimi-best ahwile seit ich kommentiert habe, aber ich würde sagen, die Funktion wird jedes Mal neu erstellt, also nie die gleiche, so wird immer neu gerendert, es sei denn, Sie wechseln in den useEffect-Body oder useCallback
user210757