Skip to content

Instantly share code, notes, and snippets.

@mweststrate
Last active July 21, 2018 13:33
Show Gist options
  • Save mweststrate/495259ef1875c45f327f2856d4b6ee01 to your computer and use it in GitHub Desktop.
Save mweststrate/495259ef1875c45f327f2856d4b6ee01 to your computer and use it in GitHub Desktop.
React api difference
// Example component: render the current value of a stream. Support switching streams during the lifecycle of the component
// N.B. code is untested
// Old:
class RenderStream extends Component {
subscription
componentWillMount() {
this.subscribeToStream(this.props.stream)
}
componentWillReceiveProps(nextProps) {
if (this.props.stream !== nextProps.stream) {
this.subscription.dispose()
this.subscribeToStream(nextProps.stream)
}
}
render() {
return <div>Current value: {this.state.value}</div>
}
componentWillUnmount() {
this.subscription.dispose()
}
subscribeToStream(stream) {
// assumption: subscribe triggers immediately
this.subscription = this.props.stream.subscribe(value => {
this.setState({ value })
}
}
}
// New
class RenderStream extends Component {
subscription
currentStream // more state needed, see below
static getDerivedStateFromProps() {
// here I would like to set up subscription
// but can't attach a listener as there is no 'this'
}
render() {
// render is harder to reason about:
// - this.state.value might not yet be known
// (imagine it is an object, then the render func would become more complex to avoid NPE)
// - the subscription at this point might be from
// a different stream then which we received the value from
//
// to fix the latter, we could set up subscription as render side effect,
// but, yucky.
return <div>Current value: {this.state.value}</div>
}
componentDidMount() {
this.subscribeToStream(this.props.stream)
}
componentDidUpdate(prevProps) {
if (prevProps.stream !== this.props.stream) {
this.subscription.dispose()
this.subscribeToStream(this.props.stream)
}
}
subscribeToStream(stream) {
this.currentStream = this.props.stream
this.subscription = this.props.stream.subscribe(value => {
this.setState({ value })
}
}
}
@JReinhold
Copy link

componentDidUpdate has a prevProps argument. Can't you use that and do the same thing as your old componentWillReceiveProps? Comparing the previous props with the current props, and update the subscription accordingly. Or am I missing something?

What I'm proposing is:

// New
class RenderStream extends Component {
	subscription

	static getDerivedStateFromProps() {
		// here I would like to set up subscription
		// but can't attach a listener as there is no 'this'
	}

	render() {
		// render is harder to reason about:
		// - this.state.value might not yet be known 
                //   (imagine it is an object, then the render func would become more complex to avoid NPE)
		// - the subscription at this point might be from
		//   a different stream then which we received the value from
		//
		// to fix the latter, we could set up subscription as render side effect,
		// but, yucky.
		return <div>Current value: {this.state.value}</div>
	}

	componentDidMount() {
		this.subscribeToStream(this.props.stream)
	}

	componentDidUpdate(prevProps) {
		//compare previous and current props, to switch streams
		//no need for an extra state variable
		if (prevProps.stream !== this.props.stream) {
			this.subscription.dispose()
			this.subscribeToStream(this.props.stream)
		}
	}

	subscribeToStream(stream) {
		this.subscription = this.props.stream.subscribe(value => {
			this.setState({ value })
		}
	}
}

@mweststrate
Copy link
Author

@mweststrate
Copy link
Author

@JReinhold, thanks for pointing out, updated the gist

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment