简体   繁体   English

如何在反应中强制从父组件重新渲染钩子?

[英]How to force a re-render of a hook from a parent component in react?

I have two files:我有两个文件:

  1. App.js应用程序.js
  2. useFetch.js使用Fetch.js

useFetch.js is a custom hook that i want to use to do requests to certain API using fetch. useFetch.js 是一个自定义钩子,我想用它来使用 fetch 对某些 API 进行请求。

This is the content of App.js:这是 App.js 的内容:

import React, { useState } from 'react';
import useFetch from './hooks/useFetch';
...
const App = () => {
  const [page, setPage] = useState(1);
  const [data, error, setUrl, isLoading, abort] = useFetch();

  const handleAction = () => {
    if (isLoading) {
      abort();
      console.log('abort signal sended');
    } else {
      setUrl(`https://reqres.in/api/users?page=${page}`);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <div
          className="App-header-button"
        >
          <Button
            variant="contained"
            color={isLoading ? 'secondary' : 'primary'}
            onClick={() => handleAction()}
          >
            {isLoading ? 'Cancel' : 'Test'}
          </Button>
        </div>
        {data && 'Data loaded'}
        <input type="text" onChange={event => setPage(event.target.value)} value={page} />
      </header>
    </div>
  );
};

export default App;

As you can see, is a simple app.如您所见,是一个简单的应用程序。 The input on the end is using to set the page to display from the API.最后的输入用于设置要从 API 显示的页面。

This is the content of my custom useFetch.js:这是我自定义的 useFetch.js 的内容:

import { useState, useEffect } from 'react';

  const useFetch = (defaultUrl) => {
  const [url, setUrl] = useState(defaultUrl);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [isLoading, setIsLoading] = useState(false);
  const [controller] = useState(new AbortController());

  const { signal } = controller;

  useEffect(() => {
    if (url) {
      setIsLoading(true);
      fetch(url, { signal })
        .then(res => res.json())
        .then((res) => {
          setData(res.data);
          setIsLoading(false);
        })
        .catch((err) => {
          setError(err);
          console.log(err.name);
          setIsLoading(false);
        });
    }
  }, [url]);

  const abort = () => {
    controller.abort();
    setIsLoading(false);
  };

  return [
    data,
    error,
    setUrl,
    isLoading,
    abort,
  ];
};

export default useFetch;

I have one problem here... this works very good and display the data when i change the page and hit the button.我这里有一个问题......这很好用,当我更改页面并点击按钮时会显示数据。 The problem is: If i push the button with the page and hit the button again with the same page, nothing happens.问题是:如果我在页面上按下按钮并在同一页面上再次按下按钮,则没有任何反应。 This have certain sense because the url aren't change, still be the same data sended to the hook.这有一定的意义,因为url没有改变,仍然是发送到钩子的相同数据。 Suppose the user wants to refresh the data with the same page hitting the button again.假设用户想要在同一页面再次点击按钮来刷新数据。 On this case i need the hook makes again the same call repeating the same url.在这种情况下,我需要钩子再次发出相同的调用,重复相同的 url。 So how can I do to force the hook to 'render' it again?那么我该怎么做才能强制钩子再次“渲染”它呢?

[UPDATE I] [更新我]

With the help of @Atul i made this changes and works.在@Atul 的帮助下,我进行了这些更改并开始工作。 I only wonder if this is a good way or the most appropriate way to achieve this.我只是想知道这是否是实现这一目标的好方法或最合适的方法。

On useFetch.js:在 useFetch.js 上:

  useEffect(() => {
    if (url && refreshFlag) {
      setIsLoading(true);
      fetch(url, { signal })
        .then(res => res.json())
        .then((res) => {
          setData(res.data);
          setIsLoading(false);
          setRefreshFlag(false);
        })
        .catch((err) => {
          setError(err);
          console.log(err.name);
          setRefreshFlag(false);
          setIsLoading(false);
        });
    }
  }, [url, refreshFlag]);

On App.js: (only showing the code of the button action)在 App.js 上:(仅显示按钮操作的代码)

  const handleAction = () => {
    if (isLoading) {
      abort();
      console.log('abort signal sended');
    } else {
      refresh(true);
      setUrl(`https://reqres.in/api/users?page=${page}`);
    }
  };

I know that @Atul alread gave you a solution to this problem,我知道@Atul 已经为您提供了解决此问题的方法,
and I used this to also make things work in my code,我用它来让代码在我的代码中工作,
but not all is said here and I needed to think about it a bit, how to implement it,但这里并没有全部说完,我需要考虑一下,如何实现它,

So I'll write this answer to make this simpler for other guys which will visit this page.所以我会写这个答案,让其他访问这个页面的人更简单。
Keep in mind that credits goes to @Atul :)请记住,功劳归@Atul :)

I will list what changes needs to be done in code above to make it work.我将在上面的代码中列出需要进行哪些更改才能使其正常工作。

First, in useFetch.ts , you want to:首先,在useFetch.ts中,您想要:

  1. add new state添加新状态
    const [refreshFlag, setRefreshFlag] = useState(true)

  2. add a function which triggers the refresh添加触发刷新的函数
    const refresh = () => {setRefreshFlag(true)}

  3. then you want to 'export' this function, not the state, so the return part of useFetch.ts should look like this:那么你想“导出”这个函数,而不是状态,所以useFetch.tsreturn部分应该是这样的:

  return [
    data,
    error,
    setUrl,
    isLoading,
    abort,
    refresh, // added this
  ];
  1. You want your useEffect to depend on the refreshFlag , and you already have that.您希望您的useEffect依赖于refreshFlag ,并且您已经拥有它。
    One thing to change to make the code nicer is, instead of doing setIsLoading(false);改变以使代码更好的一件事是,而不是执行setIsLoading(false); and setRefreshFlag(false);setRefreshFlag(false); in both try and catch , you can insted do it one time - in finallytrycatch中,你都可以尝试一次 - in finally

Now, in App.js :现在,在App.js中:

A) You want to use refresh() in place where you have refresh(true); A)您想在有refresh(true);的地方使用refresh() );

B) You haven't put in the code above that you also need to destructure refresh from useFetch , so change B)您还没有输入上面的代码,您还需要从useFetch解构refresh ,所以更改
const [data, error, setUrl, isLoading, abort] = useFetch(); to
const [data, error, setUrl, isLoading, abort, refresh] = useFetch();

--------------- Below is a tip not realated to answer, ------------------- --------------- 下面是一个无法回答的提示,--------
--------------- just something what imo makes the code cleaner ------------ --------------- 正是 imo 使代码更清晰的东西 ------------

Something to add - this is a matter of taste, but I personally dont like destructuring things like you did from useFetch .要添加的东西 - 这是一个品味问题,但我个人不喜欢像你从useFetch所做的那样解构事物。 Reasons are:原因是:

  • when you will have two API requests in one component, you will need to create unique names for variables from those hooks当您在一个组件中有两个 API 请求时,您需要为这些钩子中的变量创建唯一名称
  • when you see in code refresh you don't know that it's the function from API, you need to spend time (few seconds, but always something) to get that info当你在代码refresh中看到你不知道它是来自 API 的函数时,你需要花时间(几秒钟,但总是一些)来获取该信息

Instead what I do is make a variable for the hook inside component:相反,我所做的是为组件内部的钩子创建一个变量:
const fetch = useFetch()
(this useFetch name is bad example, in practice you will have something like (这个useFetch名称是不好的例子,在实践中你会有类似的东西
const getUsers = useGetUsers() ), const getUsers = useGetUsers() ),
and then you will use, in your code, getUsers.data , getUsers.isLoading etc.然后您将在您的代码中使用getUsers.datagetUsers.isLoading等。

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

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