不使用 useEffect Hook 获取 API 是错误的吗?

Is it wrong to fetch an API without using the useEffect Hook?

我一直这样做,但有些大学告诉我应该改用 useEffect Hook。问题是我看不到这种方法的好处,而且我认为我的方法更简洁。

import React, { useState, useEffect } from "react";

const fetchTheApi = () =>
  new Promise(res => setTimeout(() => res({ title: "Title fetched" }), 3000));

const UseEffectlessComponent = () => {
  const [data, setData] = useState();
  !data && fetchTheApi().then(newData => setData(newData));
  return <h1>{data ? data.title : "No title"}</h1>;
};

const UseEffectComponent = () => {
  const [data, setData] = useState();
  useEffect(() => {
    fetchTheApi().then(newData => setData(newData));
  }, []);
  return <h1>{data ? data.title : "No title"}</h1>;
};

const MyComponent = () => (
  <div>
    <UseEffectlessComponent />
    <UseEffectComponent />
  </div>
);

根据回复进行编辑:

我更改了代码以重新渲染,如下所示:

import React, { useState, useEffect } from 'react';

const fetchTheApi = (origin) => {
    console.log('called from ' + origin);
    return new Promise((res) =>
        setTimeout(() => res({ title: 'Title fetched' }), 3000)
    );
};

const UseEffectlessComponent = () => {
    const [data, setData] = useState();
    !data &&
        fetchTheApi('UseEffectlessComponent').then((newData) => setData(newData));
    return <h1>{data ? data.title : 'No title'}</h1>;
};

const UseEffectComponent = () => {
    const [data, setData] = useState();
    useEffect(() => {
        fetchTheApi('UseEffectComponent').then((newData) => setData(newData));
    }, []);
    return <h1>{data ? data.title : 'No title'}</h1>;
};

const MyComponent = () => {
    const [counter, setCounter] = useState(0);
    counter < 3 && setTimeout(() => setCounter(counter + 1), 1000);
    return (
        <div>
            <p>counter is: {counter}</p>
            <UseEffectlessComponent />
            <UseEffectComponent />
        </div>
    );
};

在控制台中我得到:

called from UseEffectlessComponent called from UseEffectComponent called from UseEffectlessComponent called from UseEffectlessComponent called from UseEffectlessComponent

所以,我终于发现了这种方法的好处。我有一些代码要更改...非常感谢您的回答!

现在,如果您的组件 re-renders 在设置数据之前它将尝试再次获取数据,从而导致多次获取。考虑到您只想获取一次数据而不是意外多次,最好将它放在 useEffect 中。

您的编写方式确实有效。你说的是 "If the fetch fails and the component re-renders, then try again, else don't"。我个人认为这是一个不可靠的系统 - 取决于 re-render 再试一次,并且很容易出现意外 side-effects:

  • 如果你的数据是假的怎么办?如果它失败了怎么办(你没有处理)。在这种情况下,它将继续尝试 re-fetch.

  • 如果父级连续渲染3次怎么办(很常见的情况)。在这种情况下,您的提取将在第一次提取完成之前发生 3 次。

因此,考虑到这一点,您实际上需要进行更仔细的检查,以确保您的代码不会因不使用 useEffect 而产生意外后果。另外,如果你的 fetch 想要 re-fetch on prop changes 你的解决方案也不起作用。

你应该使用useEffect,因为你所做的是anti-pattern。从react的官网上可以清楚的看到为什么要有useEffect:

Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before. https://reactjs.org/docs/hooks-effect.html

React 组件只是函数,需要一些 props 和 returns 一些 jsx。如果你想有副作用,你不应该直接在你的组件中。它应该在生命周期方法中。

想象一下您的条件检查 (!data),它是一个复杂的遍历数组等的检查。它会对性能产生更大的影响。但是 useEffect 的性能会更高,您甚至可以使用第二个参数来获得 'caching' 的结果。

在您的两个组件之间在技术上没有区别,除了条件检查将在您的版本中的每个渲染上运行。而 useEffect 只会在组件的 'mounted'、'updated' 状态下被调用。