Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux, Do I have to import store in all my containers if I want to have access to the data?

Tags:

reactjs

redux

Perhaps I am not wrapping my head around redux, but all the examples I've seen don't really access state too much between containers and so I haven't seen much usage of store.getState(), but even if you want to dispatch, you need access to store, right?

So, other than importing import store from 'path/to/store/store'

in every file that I want to getState() or "dispatch", how do I get access to that state because if I don't include it, store is undefined.

like image 799
james emanon Avatar asked Feb 09 '16 19:02

james emanon


People also ask

How do I access data from Redux store?

It's simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need. Then, inside the component, you can pass that data to a function that needs it.

Should I have multiple Redux stores?

Some valid reasons for using multiple stores in Redux might include: Solving a performance issue caused by too frequent updates of some part of the state, when confirmed by profiling the app.

Should I keep all component's state in Redux store?

Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.

How many Redux stores should you have?

In a react-redux application, it is a good practice to have only one redux store. But if for some weird/“special” reason if you have to have more than one store, you will face some problems.


1 Answers

In general you want to only make top-level container components ones that have access to the store - they will pass down any necessary data or action dispatches as props to their children components. This is the difference between a "smart" and a "dumb" component - "smart" components know about the Redux store/state, while "dumb" components just get props passed to them and have no idea about the bigger application state.

However, even just passing the store to container components can become tedious. That's why React-Redux provides one component out of the box that wraps your entire application. Check it out in the docs. This is the Provider component and when you wrap your whole app with it, you only pass the store to a component once:

import createStore from '../store';

const store = createStore()

class App extends Component {

  render() {
    return (
      <Provider store={store}>
        <MainAppContainer />
      </Provider>
    )
  }
}

As you can see here, I have a separate configuration file just for my store as there is a lot of modification you can do and for any remotely complex app, you'll find yourself doing the same for things like using compose to apply middleware.

Then any of your remaining "smart" components (generally wrappers) need to listen to the store. This is accomplished using the connect method. This allows you to map pieces of the state to your component properties as well as dispatch actions as properties.

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';

const mapStateToProps = function(state){
  return {
    something: state.something,
  }
}

const mapDispatchToProps = function (dispatch) {
  return bindActionCreators({
    getSomething: actionCreators.getSomething,
  }, dispatch)
}

class MainAppContainer extends Component {

    componentDidMount() {
      //now has access to data like this.props.something, which is from store
      //now has access to dispatch actions like this.props.getSomething
    }

    render() {
        //will pass down store data and dispatch actions to child components
        return (
               <div>
                   <ChildComponent1 something={this.props.something} />
                   <ChildComponent2 getSomething={this.props.getSomething} />
               </div>
        )
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(MainAppContainer)

Because you are always passing down dispatch actions and data to your children component as properties, you just reference those on that component with this.props.

Building off the example above, you'll see that because I passed this.props.something to ChildComponent1, it has access to the something data from the store but it does not have access to the getSomething dispatch action. Likewise, ChildComponent2 only has access to the getSomething dispatch action but not the something data. This means that you only expose components to exactly what they need from the store.

For example, because ChildComponent2 was passed down the dispatch action as getSomething, in my onClick I can call this.props.getSomething and it will call the dispatch action without needing any access to the store. In the same way it can continue to pass down getSomething to another child component and that component could call it and/or pass it down and the cycle could continue indefinitely.

class ChildComponent2 extends Component {

    render() {
        return (
            <div>
                <div onClick={this.props.getSomething}>Click me</div>
                <NestedComponent getSomething={this.props.getSomething} />
            </div>
        )
    }
}

Edit from the comments

While this doesn't pertain directly to the question, in the comments you seemed a little confused about actions. I did not actually define the action getSomething here. Instead it is usual in Redux apps to put all of your action definitions in a separate file called actionCreators.js. This contains functions that are named the same as your actions and return an object with a type property and any other methods/data that action requires. For instance, here's a very simple example actionCreators.js file:

export function getSomething() {
    return {
        type: 'GET_SOMETHING',
        payload: {
            something: 'Here is some data'
        }
    }
}

This action type is what your reducer would listen for to know which action was being fired.

like image 82
Andy Noelker Avatar answered Sep 21 '22 06:09

Andy Noelker