我在哪里可以使用钩子进行 API 调用以进行react?

IT技术 javascript reactjs react-native react-hooks
2021-02-11 22:35:04

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

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

但是在 React v16.7.0 引入 hooks 之后,基本上都是函数式组件

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

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

5个回答

是的,有一个类似(但不一样!)的替代品componentDidMountwith 钩子,它是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

例如,如果您要显示用户的个人资料页面,其中每个页面都有一个 userID 状态/属性,则应将该 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) 模式,因为它鼓励竞争条件。比如——在你的通话开始和结束之间可能会发生任何事情,你可能会得到新的props。相反,我们会推荐使用 Suspense 来获取数据

所以请继续关注悬念!

我明白了,请随时编辑答案以警告该部分:)
2021-03-26 22:35:04
这是关于如何使用 reacts useEffect 钩子的一个很好的解释。但我相信这个特定的钩子在 componentDidMount 生命周期方法实现上有一个错误,因为它有重复的调用,除非提供第二个参数。
2021-04-08 22:35:04
我并不是说你的例子不正确,我想强调的是,如果没有第二个参数作为空数组,那么 useEffect 钩子将无限运行,这是原始钩子实现的一个错误。
2021-04-11 22:35:04
@HusniJabir 你的意思是我的例子不正确?我不是传入了一个空数组作为第二个参数吗?
2021-04-12 22:35:04

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

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

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

现在您通过 id 抓取了文章。所有非快乐路径(加载、错误状态)分别由 Suspense 和Error 边界处理

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

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

我只是将其发布为一种更简单的方式来理解 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,[])空参数作为[]使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 调用。

由于最后给出的重要说明而对此进行投票:“...因此您可以在函数中添加自定义条件以避免不必要的 API 调用。” 👍
2021-04-04 22:35:04

当您使用带有钩子 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 将重新渲染组件。也来自文档:

小费

如果你熟悉阵营类生命周期方法,你能想到useEffect挂钩作为componentDidMountcomponentDidUpdatecomponentWillUnmount合并。

你也可以使用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>
  )
}