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


当前回答

忽略警告,并使用useEffect钩子和一个异步函数,就像这样:

import { useEffect, useState } from "react";

function MyComponent({ objId }) {
  const [data, setData] = useState();

  useEffect(() => {
    if (objId === null || objId === undefined) {
      return;
    }

    async function retrieveObjectData() {
      const response = await fetch(`path/to/api/objects/${objId}/`);
      const jsonData = response.json();
      setData(jsonData);
    }
    retrieveObjectData();

  }, [objId]);

  if (objId === null || objId === undefined) {
    return (<span>Object ID needs to be set</span>);
  }

  if (data) {
    return (<span>Object ID is {objId}, data is {data}</span>);
  }

  return (<span>Loading...</span>);
}

其他回答

我通读了这个问题,觉得答案中没有提到实现useEffect的最佳方法。 假设您有一个网络呼叫,并且希望在得到响应后执行一些操作。 为了简单起见,让我们将网络响应存储在一个状态变量中。 人们可能希望使用action/reducer来更新存储与网络响应。

const [data, setData] = useState(null);

/* This would be called on initial page load */
useEffect(()=>{
    fetch(`https://www.reddit.com/r/${subreddit}.json`)
    .then(data => {
        setData(data);
    })
    .catch(err => {
        /* perform error handling if desired */
    });
}, [])

/* This would be called when store/state data is updated */
useEffect(()=>{
    if (data) {
        setPosts(data.children.map(it => {
            /* do what you want */
        }));
    }
}, [data]);

参考=> https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

在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,这样你就可以根据组件的生命周期订阅和取消订阅。

只是一个关于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))

你也可以使用IIFE格式来保持内容简短

function Example() {
    const [data, dataSet] = useState<any>(null)

    useEffect(() => {
        (async () => {
            let response = await fetch('api/data')
            response = await response.json()
            dataSet(response);
        })();
    }, [])

    return <div>{JSON.stringify(data)}</div>
}

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