最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin0浏览0评论

Using React and Redux, imagine you have a ponent 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('', { 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 ponent update because nothing in the render of the ponent is affected.

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

Until recently people used to do something like:

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

But as of React v16.3 ponentWillReceiveProps() has been deprecated. In this example we can't use ponentDidUpdate() 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 ponent methods?

Thanks!

Using React and Redux, imagine you have a ponent 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./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 ponent update because nothing in the render of the ponent is affected.

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

Until recently people used to do something like:

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

But as of React v16.3 ponentWillReceiveProps() has been deprecated. In this example we can't use ponentDidUpdate() 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 ponent methods?

Thanks!

Share Improve this question asked Apr 11, 2018 at 10:29 PensierinmusicaPensierinmusica 6,97810 gold badges44 silver badges60 bronze badges 10
  • 1 Off-topic. The situation that you have some boolean flag in redux store but calling external API from within the ponent's method is very suspicious. :) – Yury Tarabanko Commented Apr 11, 2018 at 10:38
  • 1 What if you handled this outside of the ponent and in the reducer instead. Each time you update the value of bool in state you also make your API call? – Christopher Moore Commented Apr 11, 2018 at 10:38
  • @ChristopherMoore this could be handled in a Redux middleware indeed. I just wonder if there's a good pattern to handle it directly in the ponent though. – Pensierinmusica Commented Apr 11, 2018 at 11:25
  • 1 Is there a specific reason you want it handled in the ponent? As @YuryTarabanko mentions above, it's a code smell – Christopher Moore Commented Apr 11, 2018 at 11:33
  • 1 @Pensierinmusica I think your solution with shouldComponentUpdate is actually the least hacky and your cleanest, logical place to do this. Next to doing it where the changed prop es from obviously, which would be an action I assume. So why not let that action do the request? Your cornering yourself with the "in the ponent" constraint. It feels hacky because it's a very unusual scenario beyond the scope of a view rendering library. The props are changed by redux, and the api call is made with something that is not react i'm assuming. – timotgl Commented Apr 11, 2018 at 17:07
 |  Show 5 more ments

2 Answers 2

Reset to default 5

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

ponentDidUpdate(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 mon 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 ponent in two. The outer ponent would do the data fetching in ponentDidUpdate, and return <InnerComponent {...this.props} />. The inner ponent would have a shouldComponentUpdate implementation that prevents re-rendering further. Again, I wouldn't expect this to be a mon scenario, but you can do this.

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

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 ponentWillReceiveProps, 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 (ponentDidUpdate).

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

Fetching external data when props change

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

Before:

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

ponentWillReceiveProps(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;
  }

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

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

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论