Things evolve. ReactJS evolves. With version 16.3, there were several changes to the component life-cycle methods. In particular, componentWillReceiveProps
is disappearing. In its place, they say, you can use the getDerivedStateFromProps
static function. I found this a bit challenging, but I did find an interesting pattern when the component-state was dependent on fetched information.
I should mention that I had a specific goal to better encapsulate data within the component. While I could pass in all the needed data as properties, that would require the surrounding component know what to pass and how to get it. That shouldn’t necessarily be necessary; the component knows what it needs and how to retrieve it.
For example, say you have a component which accepts a phone number and displays the phone number and the state that it’s from. Certainly, you could write a simple component that accepts both pieces of information as properties.
<ShowPhoneLocation number="+12065551212" city="Seattle" />
Which might be implemented as:
class ShowPhoneLocation extends React.Component { render() { return ( <div>{this.props.number} is from {this.props.city}</div> ) } // render() } // class ShowPhoneLocation
But, since the component should be able to infer the state from the phone number (by its area code), it shouldn’t be incumbent on its container to know what it is.
class ShowPhoneLocation extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { let location = getCityFromPhone(nextProps.number) return { city: location } } render() { return ( <div>{this.props.number} is from {this.state.city}</div> ) } // render() } // class ShowPhoneLocation
That’s all well and good, but what if getCityFromPhone()
has to call a web service? We don’t want getDerivedStateFromProps()
to stall, waiting for a response. However, it is static and does not have a this
reference to the object for which it is returning state; so an asynchronous fetch
doesn’t know what object’s state to set. Instead, don’t wait for the result to save in the state, save the request’s Promise
in the state and update the state, once the promise resolves.
function getCityFromPhone(number) { return fetch('http://saas.com/get/'+number+'/city') // Returns fetch promise } class ShowPhoneLocation extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { let location = getCityFromPhone(nextProps.number) return { city: location } } componentDidUpdate() { let location = this.state.city if (location instanceof Promise) { this.setState({ city: '...waiting...' }) location.then(city => this.setState({ city: city }) ) .catch(() => this.setState({ city: 'not found' }) ) } } render() { return ( <div> {this.props.number} is from {this.state.city instanceof Promise ? '...' : this.state.city}</div> ) } // render() } // class ShowPhoneLocation
In componentDidUpdate()
you can define the completion handlers to set the object’s state, base on the returned information from the service.
It is a common pattern to perform a fetch
in componentDidMount()
. The problem is that there may not be enough information to perform the fetch, that early, or the information for the fetch changes after the component has been mounted.
I am going to miss componentWillReceiveProps()
… without it, things become a bit more convoluted but it’s going the way of the Dodo.