N
N
Nikita Shchypylov2018-03-08 01:01:08
React
Nikita Shchypylov, 2018-03-08 01:01:08

Why does React not see props from Redax in the handler when it sees it in render(){}?

There is this component:

import React, { Component } from "react";
import NewStory from "../NewStory";
import "./index.scss";
import MaterialIcon from "material-icons-react";
import history from "../../history";
import { connect } from "react-redux";

class Dashboard extends Component {
  state = {
    addNewOpen: false
  };

  handleCreateNew = () => {
    this.setState({
      addNewOpen: !this.state.addNewOpen
    });
  };

  clickHandle(e) {
    e.preventDefault();

    let targetElement = e.currentTarget.dataset.id;
    const { stories } = this.props; // ТУТ НЕ ВИДИТ

    if (stories) {
      Object.keys(stories).map(element => {
        if (targetElement === element) {
          history.push({
            pathname: "/story",
            state: { detail: element }
          });
        }
      });
    }
  }

  render() {
    const { addNewOpen } = this.state;
    const { stories } = this.props; // ТУТ ВИДИТ
    const dashClass = stories ? "dashboard not-empty" : "dashboard empty";
    let elements = stories ? (
      Object.keys(stories).map(key => {
        let imgUrl = stories[key].img ? stories[key].img : "img/no_image.jpg";
        return (
          <a
            key={key}
            href="/"
            onClick={this.clickHandle}
            data-id={key}
            className="stories__element">
            <img className="stories__element-image" src={imgUrl} alt="image" />
            <div className="overlay" />
            <h2 className="stories__element-title">{stories[key].title}</h2>
            <div className="stories__element-labels">{stories[key].labels}</div>
          </a>
        );
      })
    ) : (
      <div>Loading...</div>
    );
    const content = stories ? (
      <div className="stories">
        <div
          className="create-new stories__element"
          onClick={this.handleCreateNew}>
          <div className="overlay" />
          <h2 className="create-new__title">
            <MaterialIcon icon="add" size={48} color="#000" />
            Add new
          </h2>
        </div>
        {elements}
      </div>
    ) : (
      <div>
        <div className="create-new" onClick={this.handleCreateNew}>
          <MaterialIcon icon="alarm_add" color="#68edc6" size={100} />
          <h2 className="create-new__title">Add new</h2>
        </div>
      </div>
    );
    return (
      <div className={dashClass}>
        <NewStory isOpen={addNewOpen} />
        {content}
      </div>
    );
  }
}

const mapDispatchToProps = {};
export default connect(
  state => ({
    stories: state.stories
  }),
  mapDispatchToProps
)(Dashboard);

Initially, it worked without connect (), because I am passing the object from above. When it didn't work, I added connect(), but it still doesn't work.
Error -
main.js:92935 Uncaught TypeError: Cannot read property 'props' of undefined

What could be the problem?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Spirin, 2018-03-08
@Nikulio

You are passing the clickHandle method to the click event listener callback , when called, the method loses its context because it is not called on your object. this in this case does not refer to your object, but to undefined . Since babel adds 'use strict' when translating (otherwise, when executed in a browser, it would refer to window ). There are several ways to fix this:
1. remake the handler from a class method into a class field arrow function :
was:

clickHandle(e) {
  // some code
}

became:
clickHandle = e => {
  // some code
};

The class field arrow function receives an instance of the class as its context upon initialization and always refers to it wherever you pass it. This is an experimental JavaScript feature and is not yet in the specification. Babel is responsible for translating this construct into valid code.
The result that will be obtained after the broadcast can be viewed here . Lines 23 to 28.
2.bind it in the constructor to an instance of the class:
constructor(props) {
  super(props);
  this.clickHandle = this.clickHandle.bind(this);
}

The call to bind returns a wrapper that invokes your method using the passed argument as the context, which in our case is the class instance.
3. wrap in an arrow function in render :
When wrapped in an arrow function, the following happens: the function itself uses your object as a context, so when called, the method will be called on your object and this in the method itself will refer to the object. This option is reasonable to use only when you need to pass your arguments to the handler, in addition to event :
<ListItem
  key={item.id}
  onClick={e => this.сlickHandle(item.id, e)}
>
  {item.name}
</ListItem>

since the engine is forced to define the context for the arrow function every render, and this takes additional processor time.
How the method loses context .
Let's take an example of an object. The same happens in the case of a class, but we will use an object in the example:
var john = {
  firstName: 'John',
  getName() {
    return this.firstName;  // this - контекст вызова и это не всегда наш объект
  }
}

Scenario 1: Here we call a method on an object. The context will be our object. Scenario 2:
var foo = {
  firstName: 'foo',
};
var foo.getName = john.getName;
console.log(foo.getName()); // foo

Here we pass the method of the john object to the property of the foo object without calling it, and in the next line we call it on it. The context this time will be the foo object . There will be no error just because the foo object has a fullName property .
Scenario 3:
var bar = john.getName;
console.log(bar()); // undefined

In this case, in standard mode, the context will be window , but in strict mode, an exception will be thrown:
since this in strict mode will refer to undefined
When you pass the method to the onClick callback or any other event callback, the call proceeds like the third scenario. Therefore, you must take care that your method does not lose context by using one of the methods above.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question