Get Your Props Straight in React Redux

React with Redux is super-popular these days, especially in our office. If you’re new to React with Redux, this blogpost will help you navigate the different ways you can pass props into components while keeping data flowing only one way.

One of the really nice things about React specifically is its principle of one-way data flow: data propagates down into view components through props. Once you add Redux to the mix, components at any level in your tree of components can get props from the redux store via react-redux‘s connect function. This is a really powerful feature, but it also puts in jeopardy the one-way data flow that makes React apps desirable in the first place.

Consider that you have a component class that grabs some info from state.

class Genus_ extends React.Component<GenusProps, undefined> {
    render() {
        return (<div>{this.props.thing}</div>);
    }
}

const mapStateToProps = (state) => {
    return {
        thing: state.thing.you.want.fromState
    };
}

export const Genus = connect(mapStateToProps, undefined)(Genus_);

Here, mapStateToProps is taking the whole state object and just grabbing bit off. Very easy if you know you’ll always need a specific piece of state for any instance of the component.

For reusable components, it won’t be that easy. Say you need to write a presentational component to be a list item, and that list is represented in the state tree, like so:

{
    families: {
        Felidae: {
            genera: {
                Leopardis:{
                    numberOfToes: 5,
                    goodAtCatchingAntelope: true,
                    wouldMakeGoodPet: false,
                    isAlsoAShoeBrand: false
                },
                Puma: {
                    numberOfToes: 11,
                    goodAtCatchingAntelope: true,
                    wouldMakeGoodPet: false,
                    isAlsoAShoeBrand: true
                },
                Lynx: {
                    numberOfToes: 3, 
                    goodAtCatchingAntelope: false,
                    wouldMakeGoodPet: false, 
                    isAlsoAShoeBrand: false
                }
        },
        Canidae: {..},
        Ursidae: {..}
    }
}

In this case, if you were to follow the pattern of having container components connected to the store and pass down props to presentational components, you’d then do something like the following (assume family.js has its own mapStateToProps that puts families from state onto props).

class Family extends React.Component<undefined, undefined> {
    render() {
        const leopardis = this.props.families.Felidae.genera.Leopardis;
        const puma = this.props.families.Felidae.genera.puma;
        const lynx = this.props.families.Felidae.genera.lynx;

        return (
            <Genus name="Leopardis" numberOfToes={leopardis.numberOfToes} ... />
            <Genus name="Puma" numberOfToes={puma.numberOfToes} ... />
            <Genus name="Lynx" numberOfToes={lynx.numberOfToes} ... />
        );
    }
}

This is an ok way to get it done, but if code ought to be its own documentation, then I’d say this code is in the wrong component. Rather than stuffing all of a component’s props into it from the parent, the parent could just give the child component the identifier to the analogous structure within state, and get the props from state in mapStateToProps.

So a revised family.js gives its children the properties it needs to navigate the state tree, and looks like this:

class Family extends React.Component<undefined, undefined> {
    render() { 
        return (
            <Genus family="Felidae" name="Leopardis" />
            <Genus family="Felidae" name="Puma" />
            <Genus family="Felidae" name="Lynx" />
        );
    }
}

And that information passed to the child component can be used by defining genus.js‘s mapStateToProps with a second parameter called ownProps.

// in genus.js

const mapStateToProps = (state, ownProps) => {
    return state.families[ownProps.family].genera[ownProps.name];
}

This allows you to get updates if either ownProps or state change, and it makes your code easier to navigate since you don’t need to climb up the component tree to see what you’re giving your child component to use in the presentational layer.

A few things to keep in mind when moving forward. Your child component is now getting props from two different sources: those passed into it from its parent, and those it gets from state. If using both, keep in mind that the props available to the component are the result of Object.assign({}, ownProps, stateProps, dispatchProps), meaning if stateProps or dispatchProps (which I didn’t talk about here) have any of the same properties as ownProps, they will overwrite ownProps. Although can override this by providing a custom mergeProps function to connect, you’d need a really good reason to do so.

That wraps it up for this example. I hope you found it helpful! Please share any wisdom you’ve gained from managing growing components and state.

Conversation
  • Mark Erikson says:

    Yep, this is known as “normalization”, and is a recommended approach for handling connected components in Redux. For further info, see http://redux.js.org/docs/faq/OrganizingState.html#organizing-state-nested-data and http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html in the Redux docs, and the “Redux Techniques#Normalization” section in my React/Redux links list at https://github.com/markerikson/react-redux-links/blob/master/redux-techniques.md#selectors-and-normalization . I also wrote some about how normalization relates to performance, at http://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/ .

  • Andrew says:

    This is a fantastic pattern that I had not thought of. One of the bigger challenges I have with redux is how to create common components that can be used in multiple applications. I usually punt on the problem and use a PORC (Plain Old React Component) and store the state internally. I think this technique of passing in an identifier to let the child component know where it’s state lives in the Redux store might just be the solution I’ve been looking for.

    Thanks!

  • Comments are closed.