在 React.js 中,我应该在 componentWillMount 还是 componentDidMount 中发出初始网络请求?

IT技术 javascript reactjs
2021-03-04 19:26:35

在react文档中,它建议在componentDidMount方法中进行初始网络请求

componentDidMount()在安装组件后立即调用。需要 DOM 节点的初始化应该在这里进行。如果您需要从远程端点加载数据,这是实例化网络请求的好地方。在此方法中设置状态将触发重新渲染。

如果componentWillMount在渲染组件之前被调用,在这里发出请求并设置状态不是更好吗?如果我在 中这样做componentDidMount,则呈现组件,发出请求,更改状态,然后重新呈现组件。为什么在呈现任何内容之前提出请求不是更好?

4个回答

您应该在componentDidMount.

如果在渲染组件之前调用了componentWillMount,在这里提出请求并设置状态不是更好吗?

否,因为无论如何在组件呈现时请求都不会完成。

如果我在 componentDidMount 中这样做,则呈现组件、发出请求、更改状态,然后重新呈现组件。为什么在呈现任何内容之前提出请求不是更好?

因为任何网络请求都是异步的除非你缓存了数据(在这种情况下你根本不需要触发请求),否则你无法避免第二次渲染。您无法通过提前触发来避免第二次渲染。它不会有帮助。

在 React 的未来版本中,我们预计componentWillMount在某些情况下会触发多次,因此您应该使用componentDidMountfor network requests

我知道这现在超出了范围,因为 componentWillMount 已被弃用,但如果网络调用是同步的怎么办?
2021-04-24 19:26:35
另一种选择可能是从父组件获取网络数据,然后通过 props 将其传递给子组件,从而避免二次渲染。
2021-04-29 19:26:35
对不起,如果我的问题很愚蠢。如果我在 fetch 中的数据componentWillMount,即使它没有破坏第一次渲染的组件,`componentWillMount`中的 fetch data 函数是否会比 fetch datacomponentDidMount函数调用得早一点,导致我们获取到数据快一点?
2021-05-09 19:26:35
通常我会使用一个isFetching状态来进行网络请求。1. 最初isFetching = false,用空数据渲染。2. componentDidMount,dispatch fetch请求,设置isFetchingtrue,用<Loading >组件渲染3. 获取成功,设置isFetchingfalse,使用获取的数据进行渲染。有没有一种好的(安全的)方法来最初使用<Loading>组件进行渲染,而不是使用空数据进行渲染。谢谢😉。
2021-05-11 19:26:35

你应该使用componentDidMount.

为什么在呈现任何内容之前提出请求不是更好?

因为:

  • 您的请求几乎肯定不会在组件渲染之前完成(除非渲染大量标记,或者您处于零延迟量子纠缠连接),并且大多数情况下,组件最终将需要再次重新渲染
  • componentWillMount 在服务器端渲染期间也会调用(如果适用)

但是,如果您要问,(没有实际处理它的情况下)发起请求不是更好吗componentWillMount,我肯定会说是(ES6),我自己这样做是为了偶尔减少几毫秒的加载时间:

componentWillMount() {
    // if window && window.XMLHttpRequest
    if (!this.requestPromise) {
        this.requestPromise = new Promise(resolve => {
            // ... perform request here, then call resolve() when done.
        });
    }
}

componentDidMount() {
    this.requestPromise.then(data => ...);
}

这将在 期间开始预加载您的请求componentWillMount,但该请求仅在 中处理componentDidMount,无论该请求已经完成还是仍在进行中。

您应该在 componentDidMount 中发出请求,因为在 componentWillMount 中不应发出任何副作用请求。在 componentWillMount 中设置状态很好,如果在 componentDidMount 中设置状态,您将立即触发第二次重新渲染。

你会读到它是一种反模式 (UGHHH) 并且一些 linter 禁止它 (eslint-react-plugin),但我不会太关注它,因为有时它是与 DOM 交互的唯一方式。如果您使用关联的 babel 阶段,您可以在 willMount 或方法属性( state = { } )中设置默认状态

正如您所说,组件将已经渲染一次,但这很好,因为您可以显示某种加载器或资源正在加载的任何其他形式的信息。

class MyComp extends Component {

    // What I do with stage 0
    state = { mystate: 1 }

    // What you might want to do if you're not 
    // on the experimental stage, no need to do 
    // the whole constructor boilerplate
    componentWillMount() {
        this.setState({ mystate: 1 });
    }

    componentDidMount() {
        dispatch(yourAction());

        // It's fine to setState here if you need to access 
        // the rendered DOM, or alternatively you can use the ref
        // functions
    }

    render() {
        if (!this.props.myCollection) return <Loader />

        return (
           <div> // your data are loaded </div>
        )
    }
}

在渲染方法之前避免在生命周期钩子中获取的真正原因是因为 React 社区计划使渲染方法调用异步。

在此处查看 gaeron 的回复:https : //github.com/reactjs/reactjs.org/issues/302

现在这意味着在渲染之前在任何生命周期方法中放置一个异步操作,如 fetch(或任何与此相关的异步操作),将干扰渲染过程。

所以不像你今天想象的同步执行:

1. 构造函数<< 在这里触发 fetch() >> => 2. getDerivedStateFromProps << fetch 完成,回调通过事件循环添加到回调队列 >> => 3. render => 4. componentDidMount => 5. 回调在添加它们的顺序,以便获取回调现在运行。

它会是这样的:

1. 构造函数<< 想象在这里触发 fetch() >> => 2. getDerivedStateFromProps << 同时,获取完成并且回调被排队 >> << 想象在这里触发异步渲染()。它的回调也被排队 >> => 回调按照添加的顺序执行3. 所以 fetch 回调首先运行=> 4. 渲染回调运行=> 5. componentDidMount

这种干扰可能会导致状态更改被还原,因为渲染可能会应用一个较早的状态来覆盖由 fetch 所做的更改。

另一个原因是,componentDidMount 生命周期保证了相应组件在 DOM 上的存在,如果 fetch 试图在 DOM 可用或更新之前对其进行操作,则可能导致应用程序显示错误。