将useEffect钩子与“异步”结合使用

时间:2020-04-06 08:52:14

标签: javascript reactjs fetch react-hooks next.js

如何清理react挂钩中的react请求。我读到需要在我的钩子AbortController中输入内容,但我不知道如何操作。我使用next.js。消除此问题的最佳方法是什么?我得到这个警告:

警告:无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要修复,请取消使用useEffect清理功能中的所有订阅和异步任务。

这是我用于获取数据的自定义钩子:

import { useState, useEffect, useCallback } from 'react'
import { MOVIE_API_URL, MOVIE_KEY } from '../../config'

export const useMovieDetailsFetch = (movieId) => {
  const [state, setState] = useState({})
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(false)

  const fetchData = useCallback(async () => {
    setError(false)
    setLoading(true)

    try {
      const movieDetailsEndpoint = `${MOVIE_API_URL}movie/${movieId}?api_key=${MOVIE_KEY}`
      const result = await (await fetch(movieDetailsEndpoint)).json()
      const creditsEndpoint = `${MOVIE_API_URL}movie/${movieId}/credits?api_key=${MOVIE_KEY}`
      const creditsResult = await (await fetch(creditsEndpoint)).json()
      // Filtring in crew for directors only
      const movieDirectors = creditsResult.crew.filter(
        (member) => member.job === 'Director'
      )

      setState({
        ...result,
        movieDirectors,
        actors: creditsResult.cast,
      })
    } catch (error) {
      setError(true)
    }
    setLoading(false)
  }, [movieId])

  useEffect(() => {
   
    fetchData()
  }, [fetchData])

  return [state, loading, error]
}

1 个答案:

答案 0 :(得分:1)

以最原始的形式使用abort controller

const controller = new AbortController();
const { signal } = controller;
...

fetch(url, { signal });

...
// abort
controller.abort();

要终止正在进行中的fetch飞行中的钩子

useEffect(() => {
  const controller = new AbortController();
  const { signal } = controller;
  fetch(url, { signal });

  return () => {
    controller.abort(); // abort on unmount for cleanup
  };
}, []);

当我需要开发一种取消获取请求的方法时,我发现此article非常有用。

修改

需要将signal添加到fetch请求选项对象中。您还可以在效果内部定义async fetchData函数(这是正常的),因此将它们全部封装在效果挂钩的回调范围中。

export const useMovieDetailsFetch = (movieId) => {
  const [state, setState] = useState({})
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(false)

  useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;

    const fetchData = async () => {
      setError(false);
      setLoading(true);

      try {
        const movieDetailsEndpoint = `${MOVIE_API_URL}movie/${movieId}?api_key=${MOVIE_KEY}`;
        const result = await (
            await fetch(movieDetailsEndpoint, { signal })
          ).json();
        const creditsEndpoint = `${MOVIE_API_URL}movie/${movieId}/credits?api_key=${MOVIE_KEY}`;
        const creditsResult = await (
            await fetch(creditsEndpoint, { signal })
          ).json();
        // Filtring in crew for directors only
        const movieDirectors = creditsResult.crew.filter(
          (member) => member.job === 'Director'
        );

        setState({
          ...result,
          movieDirectors,
          actors: creditsResult.cast,
        });
      } catch (error) {
        setError(true);
      }
      setLoading(false);
    }

    fetchData();

    return () => controller.abort();
  }, [movieId]);

  return [state, loading, error];
}