【问题标题】:Have a custom hook run an API call only once in useEffect让自定义钩子在 useEffect 中只运行一次 API 调用
【发布时间】:2021-10-09 06:52:44
【问题描述】:

我的 React 应用程序中有一个自定义钩子,它使用 GET 请求从 MongoDB 数据库中获取一些数据。在我的一个组件中,我重复使用了钩子两次,每次都使用不同的函数来进行异步 API 调用。

当我查看数据库日志时,我意识到我的每个 GET 请求都被调用了两次而不是一次。例如,我的每个钩子都被调用了两次,使得 API 调用的数量变为四个而不是两个。我不确定为什么会这样。我猜异步调用会导致重新渲染不是并发的,或者我的组件中有某个地方导致重新渲染;不确定。

这是我加载组件时 MongoDB 日志中显示的内容:

我尝试传递一个空数组来限制它运行的时间,但这会阻止在重新加载时获取。有没有办法调整自定义钩子,让每个钩子的 API 调用只运行一次?

这是我正在使用的自定义钩子:

const useFetchMongoField = (user, id, fetchFunction) => {
  const [hasFetched, setHasFetched] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      if (!user) return;
      try {
        let result = await fetchFunction(user.email, id);
        setData(result);
        setHasFetched(true);
      } catch (error) {
        setError(error.message);
      }
    };

    if (data === null) {
      fetchData();
    }
  }, [user, id, fetchFunction, data]);

  return { data, hasFetched, error };
};

这是我重复使用自定义挂钩两次的组件之一。在此示例中,getPercentageReadgetNotes 是在 MongoDB 上被调用两次的函数(两次 getPercentageRead 调用和两次 getNotes 调用),尽管我倾向于使用它们中的每一个。

const Book = ({ location }) => {
  const { user } = useAuth0();
  const isbn = queryString.parse(location.search).id;

  const { data: book, hasFetched: fetchedBook } = useFetchGoogleBook(isbn);
  const { data: read, hasFetched: fetchedPercentageRead } = useFetchMongoField(
    user,
    isbn,
    getPercentageRead
  );
  const { data: notes, hasFetched: fetchedNotes } = useFetchMongoField(
    user,
    isbn,
    getNotes
  );

  if (isbn === null) {
    return <RedirectHome />;
  }
  return (
    <Layout>
      <Header header="Book" subheader="In your library" />

      {fetchedBook && fetchedPercentageRead && (         
        <BookContainer
          cover={book.cover}
          title={book.title}
          author={book.author}
          date={book.date}
          desc={book.desc}
          category={book.category}
          length={book.length}
          avgRating={book.avgRating}
          ratings={book.ratings}
          language={book.language}
          isbn={book.isbn}
          username={user.email}
          deleteButton={true}
          redirectAfterDelete={"/"}
        >
          <ReadingProgress
            percentage={read}
            isbn={book.isbn}
            user={user.email}
          />          
        </BookContainer>        
      )}

      {!fetchedBook && (
        <Wrapper minHeight="50vh">
          <Loading
            minHeight="30vh"
            src={LoadingIcon}
            alt="Loading icon"
            className="rotating"
          />
        </Wrapper>
      )}
      <Header header="Notes" subheader="All your notes on this book">
        <AddNoteButton
          to="/add-note"
          state={{
            isbn: isbn,
            user: user,
          }}
        >
          <AddIcon color="#6b6b6b" />
          Add Note
        </AddNoteButton>
      </Header>
      {fetchedNotes && (
        <NoteContainer>
          {notes.map((note) => {
            return (
              <NoteBlock
                title={note.noteTitle}
                date={note.date}
                key={note._noteID}
                noteID={note._noteID}
                bookID={isbn}
              />
            );
          })}
          {notes.length === 0 && (
            <NoNotesMessage>
              You don't have any notes for this book yet.
            </NoNotesMessage>
          )}
        </NoteContainer>
      )}
    </Layout>
  );
};

【问题讨论】:

  • GET 被调用了两次,因为你使用了钩子 2 次。删除 1 个钩子,您将看到 GET 调用一次。
  • @GiovanniEsposito OP 意识到了这一点,并询问如何使钩子中的 fetch 只运行一次,尽管钩子被多次使用
  • useEffect 的依赖数组决定了传递给它的函数何时被调用。如果任何引用的元素发生变化,该函数将再次运行。删除datafetchFunction
  • 是的,这就是重点……
  • @GiovanniEsposito OP 的问题完全合理

标签: javascript reactjs use-effect


【解决方案1】:

您在自定义钩子useFetchMongoField 中编写获取功能的方式没有表明请求已经发出并且您当前只是在等待响应的标志。因此,每当您的 useEffect 依赖数组中的任何属性发生更改时,您的请求都会被第二次、第三次或更多次发出。只要没有回应回来。 您可以在开始发送请求时设置一个 bool 标志,并在发送请求之前检查您的 useEffect 中的该标志。

可能是userisbn最初没有设置,当它们被设置时,它们都会触发重新渲染,并会触发对你的钩子的重新评估,并会触发你的@ 987654326@.

【讨论】:

    【解决方案2】:

    我能够解决这个问题。

    问题是我假设user 对象在渲染中保持不变,但它的一些属性实际上发生了变化。我只对检查该对象的email 属性不感兴趣,因此我只将user?.email 传递给解决了问题的依赖数组。

    【讨论】:

      猜你喜欢
      • 2021-09-10
      • 1970-01-01
      • 2022-10-09
      • 1970-01-01
      • 2020-01-12
      • 2019-08-24
      • 2021-10-21
      • 2022-12-16
      • 2019-12-09
      相关资源
      最近更新 更多