Skip to content
This repository has been archived by the owner on Sep 10, 2022. It is now read-only.

HOC Idea: keyed components #75

Closed
dtinth opened this issue Nov 25, 2015 · 2 comments
Closed

HOC Idea: keyed components #75

dtinth opened this issue Nov 25, 2015 · 2 comments

Comments

@dtinth
Copy link
Contributor

dtinth commented Nov 25, 2015

At @taskworld, when we write components that communicate with the store upon mounting, we must do it in three different places: componentDidMount, componentWillUnmount, and componentDidUpdate. For example:

var MessagesContainer = React.createClass({
  propTypes: {
    threadId: React.PropTypes.string.isRequired,
  },
  componentDidMount () {
    this.subscription = subscribeToThread(this.props.threadId)
  },
  componentDidUpdate (lastProps) {
    if (lastProps.threadId !== this.props.threadId) {
      if (this.subscription) this.subscription.unsubscribe()
      this.setState(this.getInitialState())
      this.subscription = subscribeToThread(this.props.threadId)
    }
  },
  componentWillUnmount () {
    if (this.subscription) this.subscription.unsubscribe()
  },
  // ...
})

What if a component can reject prop changes?

When the threadId changes, the old component that subscribes to the old thread is simply unmounted, and the new one with the new threadId is mounted in its place.

With that, we don’t need to handle the change in threadId anymore:

var MessagesContainer = React.createClass({
  propTypes: {
    threadId: React.PropTypes.string.isRequired,
  },
  componentDidMount () {
    this.subscription = subscribeToThread(this.props.threadId)
  },
  componentWillUnmount () {
    if (this.subscription) this.subscription.unsubscribe()
  },
  // ...
})

Turns out it is possible with the special key prop in React:

<MessagesContainer threadId={threadId} key={threadId} />

Now the MessagesContainer will be replaced instead of having props sent to it. Just what I want. However, other people reviewing my code may not understand why I need to put a key prop there.

Instead of relying on the users of MessagesContainer to specify the key prop to ensure correct subscribe/unsubscribe behavior, we can simply wrap MessagesContainer in a higher-order component which automatically adds the key to the wrapped component.

This is our implementation of keyed.

function keyed (propsToKey) {
  return function createKeyedComponent (Component) {
    const KeyedComponent = React.createClass({
      render () {
        return <Component {...this.props} key={propsToKey(this.props)} />
      },
    })
    return KeyedComponent
  }
}

And here is how to use it:

export default keyed(props => props.threadId)(Messages)
@acdlite
Copy link
Owner

acdlite commented Nov 30, 2015

Hmm, cool use case, but not sure if it warrants its own helper.

mapProps() gets you most of the way there already:

const keyed = (propsToKey, BaseComponent) =>
  mapProps(
    props => ({
      ...props,
      key: propsToKey(props)
    }),
    BaseComponent
  )

@dtinth
Copy link
Contributor Author

dtinth commented Dec 3, 2015

You’re right. mapProps is awesome.

Thank you!

@dtinth dtinth closed this as completed Dec 3, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants