Unhandled Rejection (TypeError): Cannot read prope

2019-07-25 08:39发布

问题:

I am using redux in my application where I am trying to do increment and decrement.

/actions/index.js

export const INCREMENT = "SCORE_INCREMENT";
export const DECREMENT = "SCORE_DECREMENT";

const scoreAction = {
  increment() {
    return {
      type: INCREMENT
    };
  },

  decrement() {
    return {
      type: DECREMENT
    };
  }
};

export default scoreAction;

/reducers/index.js

import * as actions from "../actions";

const scoreReducer = (state = 0, action) => {
  switch (action.type) {
    case actions.INCREMENT:
      return state + 1;
    case actions.DECREMENT:
      return state - 1;
    default:
      break;
  }
};

export default scoreReducer;

index.js

import { Provider } from "react-redux";
import { createStore } from "redux";
import scoreReducer from "./reducers";

let store = createStore(scoreReducer);

ReactDOM.render(
  <Provider store={store}><App /></Provider>,
  document.getElementById("root")
);

App.js

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import RoundedButton from "./RoundedButton";
import { connect } from "react-redux";
import { scoreAction } from "./actions/index";
import * as actions from "./actions/index";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      score: 0
    };
    this.clickitem = this.clickitem.bind(this);
  }

  clickitem(user) {
    var url = "http://localhost:4000/generate-random";
    fetch(url)
      .then(function(response) {
        if (response.status >= 400) {
          throw new Error("Bad response from server");
        }
        return response.json();
      })
      .then(function(data) {
        var computer = data.item;
        console.log("------------------------------------");
        console.log(user + computer);
        console.log("------------------------------------");
        if (
          (user === "Rock" && computer === "Scissors") ||
          (user === "Paper" && computer === "Rock") ||
          (user === "Scissors" && computer === "Paper")
        ) {
          console.log("------------------------------------");
          console.log("User won!!");
          console.log("------------------------------------");
          this.props.increment();
        } else if (user === computer) {
          console.log("------------------------------------");
          console.log("Tie");
          console.log("------------------------------------");
        } else {
          console.log("------------------------------------");
          console.log("Computer won!!");
          console.log("------------------------------------");
          this.props.decrement();
        }
      });
  }

  render() {
    const { store } = this.context;
    console.log("------------------------------------");
    console.log(store);
    console.log("------------------------------------");
    return (
      <div className="AppTitle">
        <b>Score:</b>
        <div>
          <RoundedButton text="Rock" clickitem={this.clickitem} />
          <RoundedButton text="Paper" clickitem={this.clickitem} />
          <RoundedButton text="Scissors" clickitem={this.clickitem} />
        </div>
      </div>
    );
  }
}

function mapStateToProp(state) {
  return { score: state };
}

export default connect(mapStateToProp, actions)(App);

I am getting an error

Unhandled Rejection (TypeError): Cannot read property 'props' of undefined
(anonymous function)
http://localhost:3000/static/js/bundle.js:18380:15
  18377 |       console.log("------------------------------------");
  18378 |       console.log("Computer won!!");
  18379 |       console.log("------------------------------------");
> 18380 |       this.props.decrement();
        |           ^  18381 |     }
  18382 |   });
  18383 | }
View source

Can anyone hint me what I am doing wrong ?

回答1:

If possible please change your action creator function to as follows:-

export const INCREMENT = "SCORE_INCREMENT";
export const DECREMENT = "SCORE_DECREMENT";


  export function increment() {
    return {
      type: INCREMENT
    };
  }

  export function decrement() {
    return {
      type: DECREMENT
    };
  }

Let me know if this works.

Also inside your clickItem function put this as the first line:-

var that = this;

Then access increment and decrement using

that.props.decrement()


回答2:

Looks like context problem. Try to use arrow functions in fetch.

  fetch(url)
      .then((response) => {
        if (response.status >= 400) {
          throw new Error("Bad response from server");
        }
        return response.json();
      })
      .then((data) => {
        var computer = data.item;
        console.log("------------------------------------");
        console.log(user + computer);
        console.log("------------------------------------");
        if (
          (user === "Rock" && computer === "Scissors") ||
          (user === "Paper" && computer === "Rock") ||
          (user === "Scissors" && computer === "Paper")
        ) {
          console.log("------------------------------------");
          console.log("User won!!");
          console.log("------------------------------------");
          this.props.increment();
        } else if (user === computer) {
          console.log("------------------------------------");
          console.log("Tie");
          console.log("------------------------------------");
        } else {
          console.log("------------------------------------");
          console.log("Computer won!!");
          console.log("------------------------------------");
          this.props.decrement();
        }
      });


回答3:

There are a few problem the first one is that you are not binding the function in which you are calling the decrement function.

Secondly, decrement is not directly available as a prop to the component but its is inside actions

Third you need to dispatch your action. You can change your code to the following

import scoreAction from "./actions/index";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      score: 0
    };
    this.clickitem = this.clickitem.bind(this);
  }

  clickitem (user)  {
    var url = "http://localhost:4000/generate-random";
    fetch(url)
      .then((response) => {
        if (response.status >= 400) {
          throw new Error("Bad response from server");
        }
        return response.json();
      })
      .then((data) => {
        var computer = data.item;
        console.log("------------------------------------");
        console.log(user + computer);
        console.log("------------------------------------");
        if (
          (user === "Rock" && computer === "Scissors") ||
          (user === "Paper" && computer === "Rock") ||
          (user === "Scissors" && computer === "Paper")
        ) {
          console.log("------------------------------------");
          console.log("User won!!");
          console.log("------------------------------------");
          this.props.increment();
        } else if (user === computer) {
          console.log("------------------------------------");
          console.log("Tie");
          console.log("------------------------------------");
        } else {
          console.log("------------------------------------");
          console.log("Computer won!!");
          console.log("------------------------------------");
          this.props.decrement();
        }
      });
  }

  render() {
    const { store } = this.context;
    console.log("------------------------------------");
    console.log(store);
    console.log("------------------------------------");
    return (
      <div className="AppTitle">
        <b>Score:</b>
        <div>
          <RoundedButton text="Rock" clickitem={this.clickitem} />
          <RoundedButton text="Paper" clickitem={this.clickitem} />
          <RoundedButton text="Scissors" clickitem={this.clickitem} />
        </div>
      </div>
    );
  }
}

function mapStateToProp(state) {
  return { score: state };
}
function mapDispatchToProps(dispatch) {
   return bindActionCreators({decrement: scoreAction.decrement, increment:  scoreAction.increment}, dispatch)
}
export default connect(mapStateToProp, mapDispatchToProps)(App);

Also change he action file to

export const INCREMENT = "SCORE_INCREMENT";
export const DECREMENT = "SCORE_DECREMENT";

const scoreAction = {
  increment: function() {
    return {
      type: INCREMENT
    };
  },

  decrement: function() {
    return {
      type: DECREMENT
    };
  }
};

export default scoreAction;