Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 604
Pensierinmusica Avatar asked Apr 11 '18 10:04

Pensierinmusica


People also ask

Which of the following is correct about prop in React?

Answer: C is the correct answer. Props are used to pass data to a component from outside in React.

Which methods are called when state or props of a component is changed?

An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered: static getDerivedStateFromProps() shouldComponentUpdate() render()

What is the pattern called in react JS?

The provider pattern in React is an advanced pattern used to share global data across multiple components in the React component tree. The provider pattern involves a Provider component that holds global data and shares this data down the component tree in the application using a Consumer component or a custom Hook.

How do you pass the component as props in React?

You can pass a component as props in React by using the built-in children prop. All elements you pass between the opening and closing tags of a component get assigned to the children prop.


2 Answers

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.

like image 194
Dan Abramov Avatar answered Oct 15 '22 19:10

Dan Abramov


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: enter image description here

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);
    }
  }
like image 30
Matan Bobi Avatar answered Oct 15 '22 20:10

Matan Bobi