Add Hooks to Class Components With Higher-Order Components

If you are using class components, but want to use hooks, you can’t do so directly. One strategy for adding them is to use adapter components. Another strategy is to use higher-order components to pass along data from hooks as props.

Example: Creating a MessageStore

Here’s our sample custom hook: We want to build a flash messaging system, similar to what’s provided by Ruby on Rails.

const useMessageStore = () => {
  const messageReducer = (state, action) => {
  switch (action.type) {
    case 'ADD':
      return [
        ...state,
        action.payload,
      ]
    case 'CLEAR':
      return []
    case 'DISMISS':
      return state.filter((message, index) => index !== action.payload)
    default:
      return state
  }
}
  const [state, dispatch] = useReducer(messageReducer, [])
  return [state, dispatch]
}

Using The Hook in A Functional Component

If we want to display or write messages to our queue from any functional component, we can do so by calling this hook:

const MyMessagingComponent = () => {
  const [messageStore, messageDispatch] = useMessageStore()
  return messageStore.map(message => <span>message.text</span>)
}

And we want to add a message from a function component, we can do it like this:

const MyButton = () => {
  const [messageStore, messageDispatch] = useMessageStore()
  const sendClickMessage = () => { 
    messageDispatch({ type: 'ADD', payload: 'Clicked the button'})
  }
  return (<button onClick={sendClickMessage}>Click Me!</button>)
}

Creating a HOC With Our Hook

Alternatively, we could create a component that passes the data from the hook along:

const withMessageStore = (WrappedComponent) => (props) => {
  const { state, dispatch } = useContext(MessageContext)
  return (<WrappedComponent
    {...props}
    messageStore={state}
    messageDispatch={dispatch}
  />)
}

Apply the HOC to Class Components

Using this method, we can now use our hooks in existing code, without having to refactor the whole thing:

class OldStodgyComponent extends React.Component {
  render() {
    return this.props.messageStore.map(message => <span>message.text</span>)
  }
}

export default withMessageStore(OldStodgyComponent)