Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux state is undefined in mapStateToProps

I am currently following this tutorial. I've hit a bit of a snag involving mapStateToProps in the following code:

import React from 'react'; import Voting from './voting'; import {connect} from 'react-redux';  const mapStateToProps = (state) => {   return {     pair: state.getIn(['vote','pair']),     winner: state.get('winner')   }; }  const VotingContainer = connect(mapStateToProps)(Voting); export default VotingContainer; 

Here is the Voting component that's imported:

import React from 'react'; import Vote from './Vote'; import Winner from './winner';  const Voting = ({pair,vote,hasVoted,winner}) =>   <div>     {winner ? <Winner winner={winner}/>  :       <Vote pair={pair} vote={vote} hasVoted={hasVoted}/>     }   </div>  export default Voting; 

It is supposed to render two buttons from the pair prop. The vote prop is a function that will be executed on click, hasVoted disables buttons when true and winner only renders the winner component as shown.

The state is expected to be an immutableJS map that looks like this:

Map({   vote:{     pair:List.of('Movie A','Movie B')   } }); 

Instead I am getting an error saying that state is undefined in the state.getIn line.

The code setting the state is in index:

const store = createStore(reducer);  const socket = io(document.location.protocol + '//' + document.location.hostname + ':8090'); socket.on('state', state => store.dispatch({   type: 'SET_STATE',   state })); 

I have logged store.getState()after setting and it is as expected but undefined in mapStateToProps. I also logged the state variable in above context and it's also as expected.

I also set the state normally and it surprisingly works!:

store.dispatch({   type: 'SET_STATE',   state: {     vote: {       pair: ['Movie A', 'Movie B']     }   } }); 

The value of state above is exactly what is received from the server

Lastly here's what my reducer looks like:

import React from 'react'; import {Map, fromJS} from 'immutable';  const reducer = (state = Map(), action) => {   switch (action.type) {     case 'SET_STATE':       return state.merge(action.state);   } }  export default reducer; 

What am I doing wrong?

EDIT: I realised that mapStateToProps is not being called after the store.dispatch(). I went through the docs for the possible reasons mapStateToProps is not being called and it's not one of them.

like image 976
esaminu Avatar asked Aug 16 '16 13:08

esaminu


People also ask

Why is Redux state undefined?

When Redux initializes it dispatches a "dummy" action to fill the state. So your counter reducer was called with state equal to undefined . This is exactly the case that "activates" the default argument. Therefore, state is now 0 as per the default state value ( state = 0 ).

What does mapStateToProps do in Redux?

As the first argument passed in to connect , mapStateToProps is used for selecting the part of the data from the store that the connected component needs. It's frequently referred to as just mapState for short. It is called every time the store state changes.

What is difference between useSelector and mapStateToProps?

The significant difference between them is that mapStateToProps passes down multiple values as props, while useSelector takes the current state as an argument, returns the required data, and stores the returned value as a single variable instead of a prop.

What is mapStateToProps and mapDispatchToProps in React Redux?

The mapStateToProps and mapDispatchToProps deals with your Redux store's state and dispatch , respectively. state and dispatch will be supplied to your mapStateToProps or mapDispatchToProps functions as the first argument.


1 Answers

You reducer doesn't have a default action in switch statement. Which is why even though you mentioned the initial state in reducer params, undefined is returned as store initial state

import React from 'react'; import {Map,fromJS} from 'immutable';  const reducer = (state = Map() ,action) => {   switch(action.type){     case 'SET_STATE': return state.merge(action.state);     default:       return state;   } }  export default reducer; 

Adding the default statement will fix the issue :)

like image 77
Deadfish Avatar answered Sep 23 '22 20:09

Deadfish