Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Redux - state returned in mapStateToProps has reducer names as properties?

Tags:

I have 2 reducers that are combined in a Root Reducer, and used in a store. First reducer 'AllTracksReducer" is supposed to return an object and the second 'FavoritesReducer' an array.

When I create a container component and a mapStateToProps method in connect, for some reason the returned state of the store is an object with 2 reducer objects which hold data, and not just an object containing correposding data, as expected.

    function mapStateToProps(state) {
       debugger:
       console.dir(state)
       //state shows as an object with 2 properties, AllTracksReducer and       FavoritesReducer. 


        return {
            data: state.AllTracksReducer.data,
            isLoading: state.AllTracksReducer.isLoading
        }
    }

export default connect(mapStateToProps)(AllTracksContainer);

so, in mapStateToProps, to get to the right state property, i have to say state.AllTracksReducer.data... But I was expecting the data to be available directly on the state object?

like image 253
nuway Avatar asked Feb 03 '17 14:02

nuway


2 Answers

Yep, this is a common semi-mistake. It's because you're using likely using ES6 object literal shorthand syntax to create the object you pass to combineReducers, so the names of the imported variables are also being used to define the state slice names.

This issue is explained in the Redux docs, at Structuring Reducers - Using combineReducers.

like image 94
markerikson Avatar answered Oct 21 '22 13:10

markerikson


Create some selectors that receive the whole state (or the reducer-specific state) and use it in your mapStateToProps function. Indeed the name you define when you combineReducers will be the topmost state keys, so your selectors should take that into account:

const getTracks = (state) => state.allTracks.data
const isLoading = state => state.allTracks.isLoading

This assumes you combine your reducers with allTracks as they key like here:

combineReducers({
  allTracks: allTracksReducer
})

And then you can use those selectors in your mapper, like

const mapStateToProps = state => ({
  isLoading: isLoading(state),
  tracks: getTracks(state)
})

There's a delicate link between your combineReducers call and your selectors. If you change the state key name you'll have to update your selectors accordingly.

It helps me to think of action creators as "setters" and selectors as "getters", with the reducer function being simply the persistence part. You call your setters (dispatching action creators) when you want to modify your state, and use your selectors as shown to get the current state and pass it as props to your components.

like image 20
CharlieBrown Avatar answered Oct 21 '22 12:10

CharlieBrown