我在哪里可以在反应中使用钩子进行 API 调用?

基本上我们在 React 类组件的 componentDidMount() 生命周期方法中进行 API 调用,如下所示

 componentDidMount(){//这里我们做API调用并相应地做setState}

但是在 React v16.7.0 中引入 hooks 后,它大多都是函数式组件

我的问题是,我们究竟需要在哪里使用钩子在功能组件中进行 API 调用?

我们有没有类似 componentDidMount() 的方法?

解决方案

是的,有一个类似的(但不一样的!)componentDidMount 用钩子替代,它是 useEffect 钩子.

其他答案并不能真正回答您在哪里可以进行 API 调用的问题.您可以通过使用 useEffect 和 传入一个空数组或对象作为第二个参数 来代替 componentDidMount() 来进行 API 调用.这里的关键是第二个论点.如果您不提供空数组或对象作为第二个参数,API 调用将在每次渲染时被调用,它实际上变成了一个 componentDidUpdate.

如文档中所述:

<块引用>

传入一个空的输入数组 [] 告诉 React 你的效果不依赖于组件中的任何值,所以效果只会在挂载时运行,在卸载时清理;它不会在更新时运行.

以下是一些需要进行 API 调用的场景示例:

严格在挂载上调用 API

尝试运行下面的代码并查看结果.

function User() {const [firstName, setFirstName] = React.useState(null);const [lastName, setLastName] = React.useState(null);React.useEffect(() => {fetch('https://randomuser.me/api/').then(results => results.json()).then(数据 => {常量 {name} = data.results[0];setFirstName(name.first);setLastName(name.last);});}, []);//<-- 这里必须传入[]!返回 (

姓名:{!firstName ||!姓 ?'正在加载...' : `${firstName} ${lastName}`}</div>);}ReactDOM.render(<User/>, document.querySelector('#app'));

<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></脚本><script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script><div id="app"></div>

每当某些 Prop/State 更改时调用 API

例如,如果您要显示用户的个人资料页面,其中每个页面都有一个用户 ID 状态/属性,您应该将该 ID 作为值传递给 useEffect 的第二个参数,以便将为新的用户 ID 重新获取数据.componentDidMount 在这里是不够的,因为如果您直接从用户 A 转到用户 B 的配置文件,则可能不需要重新安装组件.

在传统的课堂方式中,你会这样做:

componentDidMount() {this.fetchData();}componentDidUpdate(prevProps, prevState) {if (prevState.id !== this.state.id) {this.fetchData();}}

有了钩子,那就是:

useEffect(() => {this.fetchData();}, [ID]);

尝试运行下面的代码并查看结果.例如,将 id 更改为 2 以查看 useEffect 再次运行.

function Todo() {const [todo, setTodo] = React.useState(null);常量 [id, setId] = React.useState(1);React.useEffect(() => {if (id == null || id === '') {返回;}获取(`https://jsonplaceholder.typicode.com/todos/${id}`).then(results => results.json()).then(数据 => {设置待办事项(数据);});}, [ID]);//只要 id 不同,useEffect 就会触发.返回 (

<输入值={id} onChange={e =>setId(e.target.value)}/><br/><pre>{JSON.stringify(todo, null, 2)}</pre></div>);}ReactDOM.render(<Todo/>, document.querySelector('#app'));

<script src="https://unpkg.com/react@16.8.1/umd/react.development.js"></脚本><script src="https://unpkg.com/react-dom@16.8.1/umd/react-dom.development.js"></script><div id="app"></div>

您应该阅读 useEffect 所以你知道你可以/不能用它做什么.

悬念

正如 Dan Abramov 在 此 GitHub 问题 上所说:

<块引用>

从长远来看,我们将不鼓励这种 (useEffect) 模式,因为它鼓励竞争条件.例如 - 在您的通话开始和结束之间可能发生任何事情,并且您可以获得新的道具.相反,我们会推荐 Suspense 来获取数据

敬请期待 Suspense!

Basically we do API calls in componentDidMount() life cycle method in React class components like below

     componentDidMount(){
          //Here we do API call and do setState accordingly
     }

But after hooks are introduced in React v16.7.0, its all like functional components mostly

My query is, where exactly do we need to make API call in functional component with hooks?

Do we have any method for it similar like componentDidMount()?

解决方案

Yes, there's a similar (but not the same!) substitute for componentDidMount with hooks, and it's the useEffect hook.

The other answers don't really answer your question about where you can make API calls. You can make API calls by using useEffect and passing in an empty array or object as the second argument as a replacement for componentDidMount(). The key here is the second argument. If you don't provide an empty array or object as the second argument, the API call will be called on every render, and it effectively becomes a componentDidUpdate.

As mentioned in the docs:

Passing in an empty array [] of inputs tells React that your effect doesn’t depend on any values from the component, so that effect would run only on mount and clean up on unmount; it won’t run on updates.

Here are some examples for scenarios where you will need to make API calls:

API Call Strictly on Mount

Try running the code below and see the result.

function User() {
  const [firstName, setFirstName] = React.useState(null);
  const [lastName, setLastName] = React.useState(null);
  
  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        const {name} = data.results[0];
        setFirstName(name.first);
        setLastName(name.last);
      });
  }, []); // <-- Have to pass in [] here!

  return (
    <div>
      Name: {!firstName || !lastName ? 'Loading...' : `${firstName} ${lastName}`}
    </div>
  );
}

ReactDOM.render(<User />, document.querySelector('#app'));

<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

API Call Whenever Some Prop/State Changes

If you are for example displaying a profile page of a user where each page has a userID state/prop, you should pass in that ID as a value into the second parameter of useEffect so that the data will be refetched for a new user ID. componentDidMount is insufficient here as the component might not need remounting if you go directly from user A to user B's profile.

In the traditional classes way, you would do:

componentDidMount() {
  this.fetchData();
}

componentDidUpdate(prevProps, prevState) {
  if (prevState.id !== this.state.id) {
    this.fetchData();
  }
}

With hooks, that would be:

useEffect(() => {
  this.fetchData();
}, [id]);

Try running the code below and see the result. Change the id to 2 for instance to see that useEffect is run again.

function Todo() {
  const [todo, setTodo] = React.useState(null);
  const [id, setId] = React.useState(1);
  
  React.useEffect(() => {
    if (id == null || id === '') {
      return;
    }
    
    fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then(results => results.json())
      .then(data => {
        setTodo(data);
      });
  }, [id]); // useEffect will trigger whenever id is different.

  return (
    <div>
      <input value={id} onChange={e => setId(e.target.value)}/>
      <br/>
      <pre>{JSON.stringify(todo, null, 2)}</pre>
    </div>
  );
}

ReactDOM.render(<Todo />, document.querySelector('#app'));

<script src="https://unpkg.com/react@16.8.1/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.1/umd/react-dom.development.js"></script>

<div id="app"></div>

You should read up on useEffect so that you know what you can/cannot do with it.

Suspense

As Dan Abramov said on this GitHub Issue:

Longer term we'll discourage this (useEffect) pattern because it encourages race conditions. Such as — anything could happen between your call starts and ends, and you could have gotten new props. Instead, we'll recommend Suspense for data fetching

So stay tuned for Suspense!

相关文章