What is the correct pattern in React JS to invoke a component method on specific props change?

拜拜、爱过 提交于 2019-11-30 03:20:39

问题


Using React and Redux, imagine you have a component method that sends a request to an external API.

import React, { Component } from 'react';
import { connect } from 'react-redux';

class MyComp extends Component {

  boolUpdate (val) {
    fetch('http://myapi.com/bool', { val });
  }

  shouldComponentUpdate (nextProps) {
    return false;
  }

  render () {
    return <h1>Hello</h1>;
  }

}

const mapStateToProps = ({ bool }) => ({ bool });

export default connect(mapStateToProps)(MyComp);

Now let's say that you want to invoke boolUpdate() each time the bool prop changes, but this should not trigger a component update because nothing in the render of the component is affected.

What's the best way to do this in React?

Until recently people used to do something like:

componentWillReceiveProps (nextProps) {
  if (nextProps.bool !== this.props.bool) this.boolUpdate(nextProps.bool);
}

But as of React v16.3 componentWillReceiveProps() has been deprecated. In this example we can't use componentDidUpdate() either, because shouldComponentUpdate() prevents that from happening. And getDerivedStateFromProps() is a static method, so it doesn't have access to the instance methods.

So, the only option we're left with seems to be using shouldComponentUpdate() itself. Something along the lines of:

shouldComponentUpdate (nextProps) {
  if (nextProps.bool !== this.props.bool) this.boolUpdate(nextProps.bool);
  return false;
}

This looks rather "hacky" to me though, and not what shouldComponentUpdate() was designed for.

Does anybody have a better pattern to suggest?

Is there a preferred way to listen to specific prop changes and trigger component methods?

Thanks!


回答1:


If you want to run some code (e.g. data fetching) when props change, do it in componentDidUpdate.

componentDidUpdate(prevProps) {
  if (prevProps.id !== this.props.id) {
    this.fetchData();
  }
}

In your example, this won't work because shouldComponentUpdate returns false. I'd argue this is not a very common case because typically you still want to re-render if props change.

For example, if the user ID changes, you might want to show a loading indicator while the data for the new user is loading. So avoiding a re-render is not very useful in this case.

However, if you're absolutely sure you both need to prevent a re-render and need to perform a side effect like fetching data on props change, you can split your component in two. The outer component would do the data fetching in componentDidUpdate, and return <InnerComponent {...this.props} />. The inner component would have a shouldComponentUpdate implementation that prevents re-rendering further. Again, I wouldn't expect this to be a common scenario, but you can do this.




回答2:


Based on the React docs and this discussion on github, The place to fetch new data based on props change is actually componentDidUpdate.

The rendering was actually splitted to two phases, the Render Phase which is pure and creates no side effects and the Commit Phase which can run side effects, work with the DOM and schedule updates.

You can see that explained well in Dan Abramov's diagram:

Dan also mentioned:

People used to mix these two different things in componentWillReceiveProps, which is why we have to split it into a pure method (getDerivedStateFromProps) and an existing impure one where it’s okay to do side effects (componentDidUpdate).

And for the solution itself, Im attaching the example from the docs:

Fetching external data when props change

Here is an example of a component that fetches external data based on props values:

Before:

componentDidMount() {
  this._loadAsyncData(this.props.id);
}

componentWillReceiveProps(nextProps) {
  if (nextProps.id !== this.props.id) {
    this.setState({externalData: null});
    this._loadAsyncData(nextProps.id);
  }
}

After:

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.id !== prevState.prevId) {
      return {
        externalData: null,
        prevId: nextProps.id,
      };
    }
    return null;
  }

  componentDidMount() {
    this._loadAsyncData(this.props.id);
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadAsyncData(this.props.id);
    }
  }


来源:https://stackoverflow.com/questions/49772726/what-is-the-correct-pattern-in-react-js-to-invoke-a-component-method-on-specific

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!