【问题标题】:Where can I make API call with hooks in react?我在哪里可以在反应中使用钩子进行 API 调用?
【发布时间】:2019-04-12 15:11:24
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • But after hooks are introduced in React v16.7.0, there are no more class components - 澄清一下,类组件仍然存在于 React v16.7.0 reactjs.org/docs/…
  • 我同意。我的意思是这种方法是创建功能组件,因为不需要类组件,但您仍然可以创建类组件。
  • @HemadriDasari:也许您可以更新您的问题以更清楚地说明您的意思。现在所说的可能会使其他人感到困惑。谢谢你的问题,这是一个很好的问题,答案对我来说很清楚。
  • @HemadriDasari :这是一个非常有用的问题,但是“没有更多的类组件”的说法是不正确的。

标签: javascript reactjs react-native react-hooks


【解决方案1】:

是的,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(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 state/prop,您应该将该 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 在this GitHub Issue 上所说:

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

敬请期待 Suspense!

【讨论】:

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

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

那么获取数据就变得如此简单:

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

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

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

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

【讨论】:

    【解决方案3】:

    我只是将其发布为一种更简单的方式来理解 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() 在组件创建(挂载)和销毁(卸载)时触发一次,而不依赖于任何值。

    专业提示:

    此外,如果您在此 fn 中添加了 return() 某些内容,那么它将与 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 调用。

    【讨论】:

    • 投了赞成票,因为最后给出了重要的 note:“...所以您可以在函数中添加自定义条件以避免不必要的 API 调用。” ?
    【解决方案4】:

    当您使用带有 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 为 componentDidMountcomponentDidUpdatecomponentWillUnmount 合并。

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-12
        • 1970-01-01
        • 1970-01-01
        • 2021-03-08
        • 2021-11-05
        相关资源
        最近更新 更多