繁体   English   中英

使用 useEffect 挂钩获取数据时,React 组件不会在 URL 参数更改上重新渲染

[英]React component not re-rendering on URL parameter change when using useEffect hook to fetch data

这是问题:

我有一个组件意味着≈ 25不同的项目/页面具有相同的结构。 所以,当我尝试使用 React 时,我会做任何人都会做的事情,并且我将动态 URL 参数传递到我的 API 请求中(如下图所示)。

const [{ items, isLoading, isError }] = useDataApi(
`http://localhost:5000/api/teams/topspending/${params.team}`,
[],
params.team);

这只是使用为简单起见而分离的useEffect组件(如下图所示)。

const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API");
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {

    setUrl(initialUrl);

    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        const result = await axios(url, opts);

        setItems(result.data.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();
  }, [effect]);

  return [{ items, isLoading, isError }];
};

export default useDataApi;

任务很简单。 Upon clicking on a simple navigation link in the navbar to change the URL from http://localhost:5000/api/teams/topspending/Team1 to http://localhost:5000/api/teams/topspending/Team2 I am wishing that SAME 组件将在使用更新的 URL 获取新数据后重新渲染。

我已经尝试了很多事情......每次 URL 更改后,我都可以更新组件,但获取的数据仍然是旧数据!

(我正在使用 React-Router 将单个组件路由到此链接)

好的,我认为您的代码中有两个小问题。

在父 function

这是我的主要 function 将使用您的自定义挂钩。 如果您看到,我不使用插值,因为您的自定义钩子不会检测到它。 这就是为什么您的自定义挂钩中的initialUrl变量(您的 URL)永远不会改变的原因。

const App = () => {
  const [id, setId] = React.useState(1);
  const response = useDataApi(
    'https://jsonplaceholder.typicode.com/posts/' + id,
    [],
    id,
  );

  return (
    <>
      <div>My id {id}</div>
      <button onClick={() => setId(id + 1)}>Click Me!</button>
    </>
  );
};

自定义钩子内部

在我看来,您误解了 react 提供的setState function。 请记住,每次调用setState function 时都不是同步的。 我的意思是,如果您使用setUrl(initialUrl) ,那么在下一行代码中,您的 state 变量url不一定已经更新了值。 要了解更多信息,您可以阅读以下内容: https://reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous

我建议使用另一个变量来调用正确的 URL 并更改自定义挂钩的变量名称。 我在您的代码中添加了一些注释//Note:

export const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API", effect, initialUrl);
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    // Note: This not sync function
    setUrl(initialUrl);

    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        // Note: I changed this url variable of your state, to use your initialUrl variable. (this initialUrl parameter should have your UPDATED URL)
        const result = await axios(initialUrl, opts);

        setItems(result.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();
  }, [effect]);

  return [{ items, isLoading, isError }];
};

我希望,这可以帮助你..

setState 是异步的,因此无法保证在下一次渲染之前它何时会受到影响。 有多种方法可以重写代码以更可预测地工作,但是对于您提供的示例,最简单的解决方案是完全删除 url state 并在对initalUrl的调用中使用axios

这不是很好。

因此,另一种选择是保留您的url state 并添加第二个useEffect观察url

例如。

const useDataApi = (initialUrl, initialData, effect) => {
  console.log("start/top Data API");
  const [items, setItems] = useState(initialData);
  const [url, setUrl] = useState(initialUrl);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    setUrl(initialUrl);
  }, [effect]);

  useEffect(() => {
    const abortCtrl = new AbortController();
    const opts = { signal: abortCtrl.signal };

    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);
      console.log("data loading");

      try {
        console.log(url, "this should be the new Url");
        const result = await axios(url, opts);

        setItems(result.data.data);
      } catch (error) {
        setIsError(true);
      }
      console.log("data loaded...");
      setIsLoading(false);
    };

    fetchData();
    return () => abortCtrl.abort();

  }, [url])

  return [{ items, isLoading, isError }];
};

export default useDataApi;

仍然不是很好,但你想做的事吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM