3

I've implemented Redux in my React application, and so far this is working great, but I have a little question.

I have an option in my navbar to change the locale, stored in redux's state. When I change it, I expect every component to rerender to change traductions. To do this, I have to specify

locale: state.locale

in the mapStateToProps function... Which leads to a lot of code duplication.

Is there a way to implicitly pass locale into the props of every component connected with react-redux ?

Thanks in advance!

adrienharnay
  • 795
  • 2
  • 9
  • 27

3 Answers3

7

Redux implements a shouldComponentUpdate that prevents a component from updating unless it's props are changed.

In your case you could ignore this check by passing pure=false to connect:

connect(select, undefined, undefined, { pure: false })(NavBar);

For performance reasons this is a good thing and probably isn't what you want.

Instead I would suggest writing a custom connect function that will ensure locale is always added to your component props:

const localeConnect = (select, ...connectArgs) => {
  return connect((state, ownProps) => {
    return {
      ...select(state, ownProps),
      locale: state.locale
    };
  }, ...connectArgs);
};

// Simply use `localeConnect` where you would normally use `connect`
const select = (state) => ({ someState: state.someState });

localeConnect(select)(NavBar);  // props = { someState, locale }
  • This is exactly what I had in mind but couldn't figure out a way to rewrite the connect function... Thanks a lot! – adrienharnay Jun 28 '16 at 06:57
  • Hello, are you sure the code you gave me manages ownProps ? At some places in my code I replaced connect with localeConnect, but sometimes ownProps is undefined, and when I use connect again and specify locale in mapStateToProps to make it work, it's not... Thanks in advance! – adrienharnay Jun 30 '16 at 10:02
  • My apologies, I haven't used redux with props in a select before (some required reading filled me in!). It seems that using the spread operator worked on the first render, but not the 2nd. I've tested with just specifying `state` and `ownProps` and this seems to work. Updated the code, hope this works for you too! – Ashley 'CptLemming' Wilson Jun 30 '16 at 23:03
  • 1
    Hello, it now works like a charm, thanks a lot again! – adrienharnay Jul 01 '16 at 09:10
0

To cut down the duplication of code I usually just pass an arrow function to the connect method when mapping state to props, looks cleaner to me. Unfortunately though, I don't think there is another way to make it implicit as your component could subscribe to multiple store "objects".

export default connect((state) => ({
  local: state.locale
}))(component);
Zach Stoltz
  • 223
  • 1
  • 9
0

To solve this problem, you can set the Context of your parent component, and use it in your child components. This is what Redux uses to supply the store's state and dispatch function to connected React components.

In your Parent component, implement getChildContext and specify each variable's PropType.

class Parent extends React.Component {
    getChildContext() {
        return {
            test: 'foo'
        };
    }

    render() {
        return (
            <div>
                <Child />
                <Child />
            </div>
        );
    }

}

Parent.childContextTypes = {
    test: React.PropTypes.string
};

In your Child component, use this.context.test and specify its PropType.

class Child extends React.Component {
    render() {
        return (
            <div>
                <span>Child - Context: {this.context.test}</span>
            </div>
        );
    }
}

Child.contextTypes = {
    test: React.PropTypes.string
};

Here's a demo of it working.

I might as well mention that, while libraries like Redux use this, React's documentation states that this is an advanced and experimental feature, and may be changed/removed in future releases. I personally would not recommend this approach instead of simply passing the information you need in mapStateToProps, like you originally mentioned.

Michael Parker
  • 12,724
  • 5
  • 37
  • 58
  • 1
    Hello and thanks for this answer. I was aware of that possibility, but it involves more code duplication, and doesn't really solve the fact that I have to explicitly pass my variable to each of my components... Thanks anyway! – adrienharnay Jun 27 '16 at 20:52
  • I'm not sure I'm following what you're saying - the example I posted demonstrates that you don't have to explicitly pass a variable to child components by using React's Context feature. In my `render` function for the Parent component, the Child components are being rendered with no props - it's assumed that any child of the Parent will implicitly have access to the context. – Michael Parker Jun 27 '16 at 21:19
  • 1
    Yeah but you have to write `ChildrenComponent.contextTypes = { test: React.PropTypes.string };` for each child component you wish to have the context passed to props... It's the same as specifying it in `mapStateToProps` – adrienharnay Jun 28 '16 at 06:54