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

Where can I make API call with hooks in react?

基本上我们在 React class 组件中 API 调用 componentDidMount() 生命周期方法

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

但是React v16.7.0引入hooks后,大部分都是函数式组件

我的问题是,我们到底需要在哪里 API 调用带钩子的功能组件?

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

当您使用带钩子的功能组件时 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 将重新渲染组件。同样来自文档:

Tip

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

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

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

如文档中所述:

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.

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

API 在 Mount 上严格调用

尝试运行使用下面的代码并查看结果。

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 每当 Prop/State 发生变化时调用

例如,如果您要显示用户的个人资料页面,其中每个页面都有一个用户 ID 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 以便了解您 can/cannot 用它做什么。

悬疑

正如 Dan Abramov 在 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

敬请期待 Suspense!

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

然后获取数据变得非常简单:

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

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

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

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

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

我只是 post 将此作为理解 acc 的更简单方法。我的努力。感谢 Yangshun Tay post,它涵盖了几乎所有内容。

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() 生命周期与 class 组件的生命周期相同。

  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 的值发生变化时,您的钩子函数就会被触发。

还要注意:这个钩子在组件挂载的时候也会被初始调用。所以那时你的名字值可能处于初始状态,这在你看来是意想不到的。因此,您可以在函数中添加自定义条件以避免不必要的 API 调用。