Redux with render props. Typescript friendly.
npm install redux-render-prop
Redux with [render props][1]. Alternative to the connect() higher order component.
Read an introductory blog post here.
[1]: https://reactjs.org/docs/render-props.html
Very TypeScript friendly. It heavily leverages type inference to
avoid manual typing of props.
For react-redux 6.x
``sh`
npm install redux-render-prop react-redux@6 # has peer dep of react-redux 6.x
For react-redux 5.x you must use pre 0.7 versions
`sh`
npm install redux-render-prop@0.6 react-redux@5 # has peer dep of react-redux 5.x
For Typescript you will need the types too
`sh`
npm install @types/react-dom @types/react @types/react-redux
`ts
import {makeConnector} from "redux-render-prop";
import {bindActionCreators} from "redux";
// Define state as a single type
interface State {
counters: {
[name: string]: {count: number};
};
}
// Define some actions creators
const ActionCreators = {
incrementByName: (name: string) => {
return {type: "INC", name};
},
};
// Create render prop component creator with app specific types.
// There is usually only one of these per app
const createAppConnect = makeConnector({
// Component creators infer the state type from here.
//
// It is possible to return only part of the state here
// which can be handy if you have a large app and want multiple
// more specific component creators.
//
// You can also return here something other than the state
// itself. For example you could wrap it with selector helpers.
prepareState: (state: State) => state,
// Actions are prepared similarly.
prepareActions: dispatch => bindActionCreators(ActionCreators, dispatch),
});
// Create render prop component for counters.
const CounterConnect = createAppConnect({
// State type is infered from the prepareState return value
mapState: (state, ownProps: {name: string}) => ({
count: state.counters[ownProps.name].count,
}),
// Actions type is infered from the prepareActions and
// ownProps type is from the mapState ownProps
mapActions: (actions, ownProps) => ({
inc() {
actions.incrementByName(ownProps.name);
},
}),
});
// Must be wrapped with
const App = () => (
Flattening render props
If you find yourself nesting too much you can flatten the render callbacks
type safely with the
MappedState, MappedActions and
MappedStateAndActions type helpers like so:`tsx
import {MappedState, MappedActions} from "redux-render-prop";class MyComponent {
renderCounter(
data: MappedState,
actions: MappedActions,
) {
return ;
}
render() {
return {this.renderCounter} ;
}
}
`You can also use it to pass the props to class components if you need to access
the mapped state or actions from lifecycle methods.
`tsx
class ClassComponent extends React.Component<
MappedStateAndActions
> {
componentDidMount() {
// do something with this.props.count
} render() {
return
{this.props.count};
}
}export default () => (
{data => }
);
`Memoizing
For advanced high performance you may use
memoizeMapState() to
create memoized selectors on component mount.`tsx
const FooConnect = createComponent({
// The initialState is the state at the time of the component
// mount and it won't change during the component lifetime.
// Same goes for the initialOwnProps.
memoizeMapState: (initialState, initialOwnProps) => {
// using the reselect module
const selectFoosOnly = createSelector(
(s: typeof initialState) => s.list,
list =>
list.map(obj => ({
foo: obj.foo,
})),
); // Return the actual mapState function
return (state, ownProps) => {
return {
foos: selectFoosOnly(state),
};
};
},
});
``Here's a more complete example with immer-reducer: