Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are my props `undefined` when using redux and react.js?

I tried to add a state to my application that just saved a boolean when some event has passed. I can't figure out what I'm doing wrong here.

Reducer:

import * as actionTypes from '../constants/action-types.js';

const initialState = [{
  eventPassed: false
}];

export default function eventPassed(state = initialState, action) {
  switch (action.type) {
    case actionTypes.EVENT_PASSED:
      return true;
    default:
      return state;
  }
}

Action:

import * as actionTypes from '../constants/action-types';

export function eventPassed(eventPassed) {
  return {
    type: actionTypes.EVENT_PASSED,
    payload: { eventPassed: eventPassed }
  };
}

Container around component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Example from '../components/example.jsx';
import { eventPassed } from '../actions/app-actions';

class ExampleContainer extends Component {
  render() {
    return (
      <Example {...this.props} />
    );
  }
}

const mapDispatchToProps = (dispatch) => ({
  actions: bindActionCreators({
    eventPassed
  }, dispatch)
});

const mapStateToProps = (state) => ({
  eventPassed: state.eventPassed
});

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

Component:

import React, { Component, PropTypes } from 'react';

class Example extends Component {

  constructor() {
    super();
    this.action = this.action.bind(this);
  }

  componentDidMount() {
    this.props.actions.eventPassed(true);
  }

  action() {
    console.log(this.props.eventPassed);
  }

  render() {
    return (
      <div>
        <button onClick={this.action}>Action</button>
      </div>
    );
  }
}

export default Example;

When I try to log "this.props.eventPassed" in the <Example /> component it gives me "undefined". Is there something missing? This seems to be the most simple use of the store in redux.

like image 529
Jim Peeters Avatar asked Nov 16 '16 15:11

Jim Peeters


2 Answers

Why is this.props.eventPassed logged as "undefined"?:

The action function (eventPassed) you are trying to access at this point does not exist at this.props.actions.eventPassed. It actually exists solely on this.props.actions.

This is because you bound the action method to the value 'actions' in your mapDispatchToProps. This is turn provides gives access to the eventPassed action via this.props.actions.

Since this.props.actions points to eventPassed, by trying to access this.props.actions.eventPassed you are trying to access the property of 'eventPassed' on the action eventPassed. The result is that when you log for this property you receive "undefined"


Other necessary amendments:

mapDispatchToProps needs to return a value

An arrow function with a block body does not automatically return a value and therefore one must be defined. So your function needs to look like:

mapDispatchToProps = (dispatch) => {
  return { actions: bindActionCreators(eventPassed, dispatch) }
} 

Initial state is an array containing an object:

const initialState = [{
  eventPassed: false
}];

Since you're trying to reference it later as an object { eventPassed: true} and not an array of objects [ { eventPassed: true } ] it should be:

const initialState = {
  eventPassed: false
};

The reducer needs to pass back the correct updated (but unmutated) state:

export default function eventPassed(state = initialState, action) {
  switch (action.type) {
    case actionTypes.EVENT_PASSED:
      return {
        ...state,
        eventPassed: action.payload
      };
    default:
      return state;
  }
}

What you were initially doing was returning a state that was no longer an object (or in the original case an array of object) but just the value of true

like image 159
Pineda Avatar answered Nov 14 '22 12:11

Pineda


In your reducer you initialize your state with array of object has eventPassed property, meanwhile you return just the boolean and that's not correct because it replace the initial state, so the initState could be just an object holding the boolean, and you must return the state based on the payload sent with the action dispathed to be as follow:

import * as actionTypes from '../constants/action-types.js';

const initialState = {
  eventPassed: false
};

export default function eventPassed(state = initialState, action) {
  switch (action.type) {
    case actionTypes.EVENT_PASSED:
      return {
        ...state,
        eventPassed: action.payload
      };
    default:
      return state;
  }
}

Also, in your component you may need to change:

this.props.eventPassed to this.props.actions.eventPassed

like image 2
Basim Hennawi Avatar answered Nov 14 '22 10:11

Basim Hennawi