70

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

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

但是在 React v16.7.0 中引入 hooks 后,它几乎都像函数组件一样

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

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

4

5 回答 5

122

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

其他答案并没有真正回答您关于在哪里可以进行 API 调用的问题。您可以通过使用useEffect传入一个空数组或对象作为第二个参数来代替componentDidMount(). 这里的关键是第二个论点。如果您不提供空数组或对象作为第二个参数,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(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>

每当某些 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);
  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>

你应该继续阅读,useEffect这样你就知道你能/不能用它做什么。

悬念

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

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

所以请继续关注悬疑!

于 2018-11-09T03:27:05.593 回答
9

您可以使用为您提供挂钩的库,例如https://resthooks.io

然后获取您的数据变得如此简单:

const article = useResource(ArticleResource.detail(), { id });

现在你通过 id 抓取了这篇文章。所有不愉快的路径(加载、错误状态)分别由 Suspense 和Error 边界处理。

要开始遵循这个简单的指南:https ://resthooks.io/docs/getting-started/installation

压缩后只有 7kb,这将为您节省很多痛苦,并且从长远来看,由于重复代码较少,您的包大小会降低。

于 2019-03-03T19:24:25.340 回答
8

我只是将其发布为一种更简单的方式来理解 acc。对我的努力。归功于 Yangshun Tay 的帖子,它几乎涵盖了所有内容。

安装组件的 API 调用

代码:

  useEffect(() => { 
    // here is where you make API call(s) or any side effects
    fetchData('/data')
  }, [] ) /** passing empty braces is necessary */

因此,当组件创建(挂载)和销毁(卸载)时,使用useEffect(fn,[])空 args[]fn()触发一次,而不依赖于任何值。

专家提示:

此外,如果您return()对此有所了解,fn那么它将componentWillUnmount()与类组件的生命周期相同。

  useEffect(() => { 
   fetchData('/data')
   return () => {
    // this will be performed when component will unmount
    resetData()
   }
  }, [] )

某些值更改时的 API 调用

如果您希望在某些值更改时调用 API,只需将该变量(存储值)传递到useEffect().

 useEffect(() => {
  // perform your API call here
  updateDetails();
 },[prop.name]) /** --> will be triggered whenever value of prop.name changes */

这将确保无论何时prop.name更改值,都会触发钩子中的函数。

还要注意:这个钩子也会在组件被安装时被调用。因此,那时您的 name 值可能处于初始状态,这在您看来是一种意外。因此,您可以在函数中添加自定义条件以避免不必要的 API 调用。

于 2020-08-15T07:47:22.980 回答
2

当您使用带有 hooks API 的功能组件时,您可以使用该useEffect()方法来产生副作用。每当由于这些副作用而更新状态时,组件都会重新渲染。

来自文档的示例。

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

例如,您可以调用setCount异步请求的回调函数。当回调被执行时,状态将被更新,React 将重新渲染组件。同样来自文档:

小费

如果您熟悉 React 类生命周期方法,您可以将 useEffect Hook 视为componentDidMount, componentDidUpdate, 和 componentWillUnmount组合。

于 2018-11-09T02:58:21.443 回答
2

你也可以use-http像这样使用:

import useFetch from 'use-http'

function App() {
  // add whatever other options you would add to `fetch` such as headers
  const options = {
    method: 'POST',
    body: {}, // whatever data you want to send
  }

  var [data, loading, error] = useFetch('https://example.com', options)

  // want to use object destructuring? You can do that too
  var { data, loading, error } = useFetch('https://example.com', options)

  if (error) {
    return 'Error!'
  }

  if (loading) {
    return 'Loading!'
  }

  return (
    <code>
      <pre>{data}</pre>
    </code>
  )
}
于 2019-04-17T18:01:44.607 回答