My simple HoC rename component

March 26, 2019

I’m currently working on a front-end project that uses both React JS and Angular JS. Yes, that sounds a bit crazy but the project is evolving from Angular to React, and this is the difficult transition phase.

We are using $ngRedux to manage our Redux application state, which presented an issue to me today when I created a new React component in this AngularJS dominated codebase.

react2angular

We use react2angular (full disclosure, I am a contributor) to embed React components in AngularJS. One of the interesting parts about this is dealing with Angular’s dependency injections. $ngRedux is served to components as a dependency and injected at runtime. We can access it with react2angular like so:

import angular from 'angular';
import { react2angular } from 'react2angular';
import MyReactComponent from './MyReactComponent';

export const MyReactComponentModule = angular
  .module('my-project.components.myReactComponent', [])
  .component('myReactComponent', react2angular(MyReactComponent, [], ['$translate', '$ngRedux']))
  .name;

We can discuss $translate another day.

So my React component uses Redux, and thus I use the very useful connect() function to transform some useful stuff from the store to my component, like actions and props.

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

The problem

Here’s what happened with my code above:

Invariant Violation: Could not find "store" in either the context or props of "Connect(MyReactComponent)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(MyReactComponent)".

ugh.

connect() expects a prop called store in the component. This prop is the redux store which provides the data and the dispatcher to the component. However, we’re using $ngRedux via dependency injection.

I searched around the redux codebase, issues, and docs to see if I can change the expected prop name. I was unsuccessful. It doesn’t mean it’s not possible, it’s probably my lack of ability to find it (which you could argue means a lack of concise documentation).

So here’s how I solved it, with a very heavy-handed higher-order component:

// This HOC simply renames $ngRedux to store (because this is used with react2angular)
const mapNgReduxToStore = WrappedComponent => {
  const MappedComponent = ({ $ngRedux, ...rest }) => {
    return (<WrappedComponent {...rest} store={$ngRedux} />);
  };
  MappedComponent.displayName = `WithStore(${WrappedComponent.displayName})`;
  MappedComponent.propTypes = {
    $ngRedux : PropTypes.object,
  };
  return MappedComponent;
};

export default lodash.flowRight(
  mapNgReduxToStore,
  connect(mapStateToProps, mapDispatchToProps)
)(MyReactComponent);

(If you’re new to compose/flowRight, it’s just a simple way to layer HoCs for a component).

There we have it! It feels heavy handed, and perhaps I should try and raise an issue with react-redux to allow a prop name change. But this gets the job done and I hope it serves someone well.