SudoPlz SudoPlz - 1 month ago 13
Javascript Question

How to reduce react-redux boilerplate - I try creating a ComponentFactory but I'm getting react-redux errors

I thought of creating a factory function called

createScreen
for reducing the boilerplate required by
react-redux
.

It looks like this:

ParentScreenFactory.js

export default function createScreen(stateActions = []) {
class ParentScreen extends React.Component {

}

function mapStateToProps(state) {
return {
...state,
};
}

function mapDispatchToProps(dispatch) {
const creators = Map()
.merge(...stateActions)
.filter(value => typeof value === 'function')
.toObject();

return {
actions: bindActionCreators(creators, dispatch),
dispatch,
};
}

return connect(mapStateToProps, mapDispatchToProps)(ParentScreen);
}


Child.js

const ParentScreen = createScreen([
routingActions,
authActions,
]);

class Child extends ParentScreen {

constructor(props) { // <-- error on this line
super(props);
}

render() {
return (
<View/>
);
}
}
export default Child;


for some reason though, I get
undefined is not an object (evaluating 'context.store')
.
Stacktrace:

Connect(ParentScreen)
connect.js:129


which is this line of code
_this.store = props.store || context.store;
.
Any obvious mistakes you see here?
Other than that do you have any better idea about how to reduce all that boilerplate code?

Thank you.

Answer

Everything will be simpler if you work with the actual component class, rather than trying to extend the empty connected one (this is the class you're actually extending).

If you want your component to work predictably, then you need to connect your component directly. Try returning a function from your factory instead.

export default function createScreen(stateActions = []) {
  return (Component) => {
    // ...
    return connect(mapStateToProps, mapDispatchToProps)(Component);
  };
}

Then your instantiation starts to look something like this.

class Child extends React.Component {
  // ...
}

const ParentScreen = createScreen([
  routingActions,
  authActions,
]);

export default ParentScreen(Child);

If you want to share some behaviour between all components, then you're better off using a higher-order-component.

function withCommonBehaviour(Component) {
  return (props) => {
    let newProps = doSomething(props);
    return <Component {...newProps} />;
  };
}

Then just hook that up inside your createScreen function.

// ...
let CommonComponent = withCommonBehaviour(Component);
return connect(mapStateToProps, mapDispatchToProps)(CommonComponent);