我正在尝试下面的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


当前回答

只是一个关于purescript语言如何AWESOME处理这个问题的陈腐效果与Aff单子

没有PURESCRIPT

你必须使用AbortController

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>;
}

或者陈旧(来自NoahZinsmeister/web3-react的例子)

function Balance() {
  const { account, library, chainId } = useWeb3React()

  const [balance, setBalance] = React.useState()
  React.useEffect((): any => {
    if (!!account && !!library) {      
      let stale = false
      
      library 
        .getBalance(account)
        .then((balance: any) => {
          if (!stale) {
            setBalance(balance)
          }
        })
        .catch(() => {
          if (!stale) {
            setBalance(null)
          }
        })

      return () => { // NOTE: will be called every time deps changes
        stale = true
        setBalance(undefined)
      }
    }
  }, [account, library, chainId]) // ensures refresh if referential identity of library doesn't change across chainIds

  ...

与PURESCRIPT

检查如何useAff杀死它的Aff在清理功能

Aff被实现为一个状态机(没有承诺)

但与我们相关的是:

你可以把你的AbortController放在这里 如果错误被抛出到光纤或光纤内部,它将停止运行Effects(未测试)和Affs(从第二个例子开始,它将不运行,因此它将not setBalance(balance))

其他回答

请试试这个

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

这里可以使用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>;
}

try

const MyFunctionnalComponent: React。FC = props => { useEffect(() => { //使用IIFE (async函数anyNameFunction() { 等待loadContent (); }) (); },[]); 返回< div > < / div >; };

使用自定义库提供的useAsyncEffect钩子,安全地执行异步代码并在效果中发出请求变得微不足道,因为它使您的代码可自动取消(这只是功能列表中的一件事)。查看带有JSON抓取的Live Demo

import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpFetch from "cp-fetch";

/*
 Notice: the related network request will also be aborted
 Checkout your network console
 */

function TestComponent(props) {
  const [cancel, done, result, err] = useAsyncEffect(
    function* () {
      const response = yield cpFetch(props.url).timeout(props.timeout);
      return yield response.json();
    },
    { states: true, deps: [props.url] }
  );

  return (
    <div className="component">
      <div className="caption">useAsyncEffect demo:</div>
      <div>
        {done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
      </div>
      <button className="btn btn-warning" onClick={cancel} disabled={done}>
        Cancel async effect
      </button>
    </div>
  );
}

export default TestComponent;

同样的演示使用了axios

对于使用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