Search code examples
javascriptreactjsreduxreact-reduxreact-rails

Creating a reusable Redux Wrapper


I am working on a RoR project that uses webpacker and react-rails. We are slowly moving away from haml to React. Because of this, each page we are doing react_component('page_to_render') We are also using Redux and I had been calling my component directly in the Provider and it was working fine. Because of the way we are using this, each page will in a sense be it's own React app, I am trying to make a reusable component to pass the store. My thought was the following:

import React, { Component } from 'react'
import { Provider } from 'react-redux'

import store from './store'

class ReduxState extends Component {
  render() {
    return (
      <Provider store={store}>
        {this.props.children}
      </Provider>
    )
  }
}
export default ReduxState

So for each page, we'd just have to wrap it in <ReduxState></ReduxState>, but when I do this I get:

Could not find "store" in the context of "Connect(BillingPage)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(BillingPage) in connect options.

It seems that store should be passed down to anything wrapped in but that isn't the case. I feel like I might be missing something about how Redux works. Is there a better way to approach this?


Solution

  • After reading more into my errors, I realized that I am returning the Provider within JSX and that the BillingPage class itself has no reference to ReduxState

    I realized I needed a HOC. Solution below:

    import React, { Component } from 'react'
    import { Provider } from 'react-redux'
    
    import store from './store'
    
    const ReduxState = WrappedComponent => {
      return class extends Component {
        render() {
          return (
            <Provider store={store}>
              <WrappedComponent {...this.props} />
            </Provider>
          )
        }
      }
    }
    
    export default ReduxState
    
    const composedHoc = compose(
      ReduxState,
      connect(
        mapStateToProps,
        {
          addBill,
          removeBill
        }
      )
    )
    
    export default composedHoc(BillingPage)