我正在尝试下面的useEffect示例:

useEffect(async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}, []);

我在控制台得到这个警告。但我认为,对于异步调用,清理是可选的。我不知道为什么我得到这个警告。链接沙盒为例。https://codesandbox.io/s/24rj871r0p


当前回答

对于使用React Hooks从外部API中获取,你应该调用一个从useEffect钩子内部的API中获取的函数。

是这样的:

async function fetchData() {
    const res = await fetch("https://swapi.co/api/planets/4/");
    res
      .json()
      .then(res => setPosts(res))
      .catch(err => setErrors(err));
  }

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

我强烈建议你不要在useEffect钩子中定义你的查询,因为它会被无限次地重新渲染。由于不能将useEffect设置为异步,所以可以将其中的函数设置为异步。

在上面所示的例子中,API调用在另一个单独的异步函数中,因此它确保调用是异步的,并且只发生一次。此外,useEffect的依赖数组([])是空的,这意味着它的行为就像React类组件中的componentDidMount一样,它只会在组件被挂载时执行一次。

对于加载文本,你可以使用React的条件渲染来验证你的帖子是否为空,如果是,渲染一个加载文本,否则,显示帖子。当你完成从API获取数据并且post不为空时,else将为真。

{posts === null ? <p> Loading... </p> 
: posts.map((post) => (
    <Link key={post._id} to={`/blog/${post.slug.current}`}>
      <img src={post.mainImage.asset.url} alt={post.mainImage.alt} />
      <h2>{post.title}</h2>
   </Link>
))}

我看到你已经在使用条件渲染,所以我建议你深入了解它,特别是验证对象是否为空!

如果您需要更多关于使用Hooks使用API的信息,我建议您阅读以下文章。

https://betterprogramming.pub/how-to-fetch-data-from-an-api-with-react-hooks-9e7202b8afcd

https://reactjs.org/docs/conditional-rendering.html

其他回答

在React提供更好的方法之前,你可以创建一个帮助器useEffectAsync.js:

import { useEffect } from 'react';


export default function useEffectAsync(effect, inputs) {
    useEffect(() => {
        effect();
    }, inputs);
}

现在你可以传递一个async函数:

useEffectAsync(async () => {
    const items = await fetchSomeItems();
    console.log(items);
}, []);

更新

如果您选择这种方法,请注意这是一种糟糕的形式。当我知道这是安全的时候,我就会这样做,但这总是糟糕的方式和随意的。

悬疑的数据获取,仍处于实验阶段,将解决一些情况。

在其他情况下,您可以将异步结果建模为事件,以便您可以根据组件生命周期添加或删除侦听器。

或者你可以将异步结果建模为一个Observable,这样你就可以根据组件的生命周期订阅和取消订阅。

正确执行并避免错误:“警告:无法在未挂载的…上执行React状态更新…”


 useEffect(() => {
    let mounted = true;
    (async () => {
      try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        const newPosts = json.data.children.map(it => it.data);
        if (mounted) {
          setPosts(newPosts);
        }
      } catch (e) {
        console.error(e);
      }
    })();
    return () => {
      mounted = false;
    };
  }, []);

或外部函数和使用对象


useEffect(() => {
  let status = { mounted: true };
  query(status);
  return () => {
    status.mounted = false;
  };
}, []);

const query = async (status: { mounted: boolean }) => {
  try {
    const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
    const json = await response.json();
    const newPosts = json.data.children.map(it => it.data);
    if (status.mounted) {
      setPosts(newPosts);
    }
  } catch (e) {
    console.error(e);
  }
};

或中止控制器


 useEffect(() => {
    const abortController = new AbortController();
    (async () => {
      try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`, { signal: abortController.signal });
        const json = await response.json();
        const newPosts = json.data.children.map(it => it.data);
        setPosts(newPosts);
      } catch (e) {
        if(!abortController.signal.aborted){
           console.error(e);
        }
      }
    })();
    return () => {
      abortController.abort();
    };
  }, []);




这里可以使用Void运算符。 而不是:

React.useEffect(() => {
    async function fetchData() {
    }
    fetchData();
}, []);

or

React.useEffect(() => {
    (async function fetchData() {
    })()
}, []);

你可以这样写:

React.useEffect(() => {
    void async function fetchData() {
    }();
}, []);

它更干净,更漂亮。


异步效果可能会导致内存泄漏,因此在组件卸载时执行清理非常重要。在fetch的情况下,它可能是这样的:

function App() {
    const [ data, setData ] = React.useState([]);

    React.useEffect(() => {
        const abortController = new AbortController();
        void async function fetchData() {
            try {
                const url = 'https://jsonplaceholder.typicode.com/todos/1';
                const response = await fetch(url, { signal: abortController.signal });
                setData(await response.json());
            } catch (error) {
                console.log('error', error);
            }
        }();
        return () => {
            abortController.abort(); // cancel pending fetch request on component unmount
        };
    }, []);

    return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

最简单的方法是使用use-async-effect中的useAsyncEffect 你可以在NPM上找到它。

const ProtectedRoute = ({ children }) => {

    const [isAuth, setIsAuth] = useState(false);

    useAsyncEffect(async () => {
        try {
            const data = await axios("auth");
            console.log(data);
            setIsAuth(true);
        } catch (error) {
            console.log(error);
        }
    }, []);



    if (!isAuth)
        return <Navigate to="/signin" />

    return children;

}

请试试这个

 useEffect(() => {
        (async () => {
          const products = await api.index()
          setFilteredProducts(products)
          setProducts(products)
        })()
      }, [])