Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-redux: how do you access the store from 'anywhere' without importing it?

In the react-redux documentation it states that when using React-redux and connect() importing the store is not recommended. It's an anti-pattern.

http://redux.js.org/docs/faq/StoreSetup.html

Similarly, while you can reference your store instance by importing it directly, this is not a recommended pattern in Redux. If you create a store instance and export it from a module, it will become a singleton. This means it will be harder to isolate a Redux app as a component of a larger app, if this is ever necessary, or to enable server rendering, because on the server you want to create separate store instances for every request.

With React Redux, the wrapper classes generated by the connect() function do actually look for props.store if it exists, but it's best if you wrap your root component in and let React Redux worry about passing the store down. This way components don't need to worry about importing a store module, and isolating a Redux app or enabling server rendering is much easier to do later.

How, then, do I access the store from any component of my choosing(even deep down in the application) once I've properly wired in the store to my app? My code properly connects App but I can't get access to the store from any child components at all. store.dispatch() is null, store.getState() is null, etc. I feel that the documentation is lacking in this regard. It's said to be magic but I'd like to know how to use the magic. Do I need to write mapDispatchToProps() again and again for every single container component? A use case would be the currentUser prop that would be available to every single child component in the application. I'd like to pass that down from App to every single child.

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);//App is now a connected component, that part is working

Inside App I have a Login component, and I'd like to dispatch an action inside it. But I need a reference to store, but apparently I'm not supposed to import it.

like image 800
user798719 Avatar asked Mar 27 '17 19:03

user798719


Video Answer


1 Answers

This is where the concept of containers come into play.

Suppose you wanted to render a Login component inside of your App. You will make a connected container.

Step 1 is to create a simple action:

const LOGIN_ATTEMPT = 'auth/LOGIN_ATTEMPT';

export const login = name => ({
  type: LOGIN_ATTEMPT,
  payload: { name },
});

You will now use react-redux in order to connect this action to your "presentational component". This will come through to the component as a prop.

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { login } from 'actions/auth';       // your action
import Login from 'components/auth/Login';  // your component to connect it to.

// state refers to the "current state" of your store.
const mapStateToProps = state => ({ currentUser: state.auth.user });

// dispatch refers to `store.dispatch`
const mapDispatchToProps = dispatch => {
  // calling this is like calling `store.dispatch(login(...params))`
  login: bindActionCreators(login, dispatch);
}

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

This connect function will take these two functions as parameters. You can now import this connected container and use it with the functions "bound" to it as properties.

Example component below.

export default class Login extends Component {
  static propTypes = {
    // properties below come from connect function above.
    currentUser: PropTypes.shape({
      name: PropTypes.string.isRequired,
    }).isRequired,
    login: PropTypes.func.isRequired,
  };

  state = { name: "" };

  onChange = ({ target: { value: name } }) => this.setState({ name });

  onSubmit = e => {
    e.preventDefault();
    login(this.state.name);
  };

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <input
          placeholder="name"
          onChange={this.onChange}
          value={this.state.name}
        />
      </form>
    );
  }
}

Notice, you never had to reference the store.

like image 192
corvid Avatar answered Nov 08 '22 13:11

corvid