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


当前回答

使用自定义库提供的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

其他回答

你也可以使用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>
}

忽略警告,并使用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>);
}

正确执行并避免错误:“警告:无法在未挂载的…上执行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();
    };
  }, []);




最简单的方法是使用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;

}

当你使用一个async函数

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

它返回一个promise,而useEffect并不期望回调函数返回promise,而是期望什么都不返回或返回一个函数。

作为警告的解决方法,您可以使用自调用异步函数。

useEffect(() => {
    (async function() {
        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);
        }
    })();
}, []);

或者为了更简洁,你可以定义一个函数,然后调用它

useEffect(() => {
    async function fetchData() {
        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);
        }
    };
    fetchData();
}, []);

第二种解决方案将使其更易于阅读,并将帮助您编写代码,以便在触发新请求时取消以前的请求或将最新的请求响应保存在状态中

工作codesandbox