GYTO
GYTO

Reputation: 500

React-Redux TypeError: Cannot read property 'setStatus' of undefined

I am new to React and Redux and I am trying to change a drop-down field value onChange, but when I selecting the value I am getting. It looks like I am following Basics of the Redux https://redux.js.org/basics, even though I researched for days to find a better solution and how to rewrite an Action function, but looking on the documentation is pretty much same code.

TypeError: Cannot read property 'setStatus' of undefined

My index.js

// @flow
import React from 'react';
import Select from 'react-select'
import {connect} from 'react-redux'

import {
  setStatus,
} from '../../actions'

type Props = {
  status: { value: ?string, label: ?string },
  setStatus: Function,
}

type State = {}

// Select Invoice Type
const statusOptions = [
  {value: 'paid', label: 'Paid'},
  {value: 'due', label: 'Due'},
  {value: 'overdue', label: 'Overdue'},
  {value: 'onhold', label: 'On Hold'}
];

class Dashboard extends React.Component<Props, State> {
  state: State;

  constructor(props: Props) {
    super(props);
    this.state = {}
  }

  handleChangeStatus(value: { value: ?string, label: ?string }) {
    console.log(value)
    if (value)
      this.props.setStatus(value);
    else
      this.props.setStatus({value: 'paid', label: 'Paid'});
  };

  render() {
    return (
      <div>
        {/* Select Invoice Status */}
        <Select
          name="status"
          value={this.props.status}
          options={statusOptions}
          searchable={false}
          onChange={this.handleChangeStatus}
        />
      </div>
    )
  }
}

function mapStateToProps(state, ownProps) {
  return {
    status: state.status,
  }
}

function mapDispatchToProps(dispatch) {
  return {
    setStatus: (statusObj) => dispatch(setStatus(statusObj)),
  }
}

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

My `/action/index.js/

// @flow

import {
  SET_INVOICE_STATUS,
} from '../constants';

export type Action = {
  status?: Object,
}

export function setStatus(status: Object): Action {
  return {
    type: SET_INVOICE_STATUS,
    status
  }
}

My /constant/index.js

// @flow

// Dashboard Implementation
export const SET_INVOICE_STATUS: string = "SET_INVOICE_STATUS";

And finally my reducer/index.js

// @flow

import {SET_INVOICE_STATUS} from "../constants";
import type {Action} from "../actions";

type State = Object;

export default function statusReducer(state: State = {value: 'paid', label: 'Paid'}, action: Action) {
  if (action.type === SET_INVOICE_STATUS) {
    return action.status;
  }

  return state;
}

UPDATE SEP 2, 2018

So after a week of learning the babel and plugins that I can use for my project I find a solution to install a plugin to the babel that helps to create an error function like handleChange = (event) => {} its called babel-plugin-transform-class-properties which is located here https://babeljs.io/docs/en/babel-plugin-transform-class-properties/. Thanks to Bhojendra Rauniyar for binding help.

Upvotes: 1

Views: 553

Answers (2)

Kevin Danikowski
Kevin Danikowski

Reputation: 5196

Like @Bhojendra said, you can do this one of two ways (both need to bind the handleChangeStatus function. First is to use an arrow function:

 handleChangeStatus = (value: { value: ?string, label: ?string }) => { //binds with arrow func
    console.log(value)
    if (value)
      this.props.setStatus(value);
    else
      this.props.setStatus({value: 'paid', label: 'Paid'});
  };

Or two, bind at the constructor:

    constructor(props: Props) {
        super(props);
        this.state = {}
        this.handleChangeStatus = this.handleChangeStatus.bind(this) // binding this to your function
      }

You do not need to do both, only one. Arrow function is easiest but both work.

Upvotes: 0

Bhojendra Rauniyar
Bhojendra Rauniyar

Reputation: 85545

When you do:

this.props.setStatus(value);

this is undefined. Because, your method is not bound. So, you need to bind this. There are different methods, you may try to bind this inside the constructor:

this.handleChangeStatus = this.handleChangeStatus.bind(this)

Upvotes: 4

Related Questions