Задать вопрос
Nikulio
@Nikulio
NaN !== NaN

Почему пропсы из Редакса в обработчике Реакт не видит, когда в render(){} видит?

Есть такой компонент :

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);


Изначально работало без connect(), ибо я передаю объект сверху. Когда не вышло, добавил connect(), но все равно не работает.
Еррор -
main.js:92935 Uncaught TypeError: Cannot read property 'props' of undefined


В чем может быть проблема?
  • Вопрос задан
  • 273 просмотра
Подписаться 1 Простой Комментировать
Помогут разобраться в теме Все курсы
  • Яндекс Практикум
    React-разработчик
    3 месяца
    Далее
  • Merion Academy
    Frontend-разработка на React
    4 месяца
    Далее
  • ProductStar
    React: отточите навыки интерфейсной разработки
    6 недель
    Далее
Решения вопроса 2
rockon404
@rockon404 Куратор тега React
Frontend Developer
Вы передаете метод clickHandle в колбек слушателя события click, при вызове метод теряет контекст, так как вызывается не на вашем объекте. this в таком случае ссылается не на ваш объект, а на undefined. Так как babel при трансляции добавляет 'use strict' (иначе, при выполнении в браузере, ссылался бы на window). Исправить это можно несколькими способами:
1. переделать обработчик из метода класса в class field arrow function:
было:
clickHandle(e) {
  // some code
}

стало:
clickHandle = e => {
  // some code
};

class field arrow function получает контекстом экземпляр класса при инициализации и всегда ссылается на него, куда бы вы ее не передали. Это экспериментальная возможность JavaScript и в спецификации ее пока нет. За трансляцию этой конструкции в валидный код отвечает babel.
Результат, который будет получен после трансляции, можно посмотреть тут. Строки с 23 по 28.

2.забиндить его в конструкторе на экземпляр класса:
constructor(props) {
  super(props);
  this.clickHandle = this.clickHandle.bind(this);
}

Вызов bind возвращает обертку, которая вызывает ваш метод, испльзуя переданный аргумент как контекст, в нашем случае это экземпляр класса.

3. обернуть в стрелочную функцию в render:
<SomeComponent onClick={() => this.сlickHandle()} /> // контекст не будет потерян

При оборачивании в стрелочную функцию происходит следующее: сама функция использует как контекст ваш объект, поэтому при вызове метод будет вызван на вашем объекте и this в самом методе будет ссылаться на объект. Этот вариант разумно использовать, только тогда, когда в хандлер необходимо передать свои аргументы, помимо event:
<ListItem
  key={item.id}
  onClick={e => this.сlickHandle(item.id, e)}
>
  {item.name}
</ListItem>

так как движок вынужден определять контекст для стрелочной функции каждый рендер, а на это уходит дополнительное процессорное время.

Как метод теряет контекст.
Разберем на примере объекта. То же самое происходит в случае с классом, но мы будем использовать в примере объект:
var john = {
  firstName: 'John',
  getName() {
    return this.firstName;  // this - контекст вызова и это не всегда наш объект
  }
}


Сценарий 1:
console.log(john.getName()); // john
Тут мы вызываем метод на объекте. Контекстом будет наш объект.

Сценарий 2:
var foo = {
  firstName: 'foo',
};
var foo.getName = john.getName;
console.log(foo.getName()); // foo

Тут мы передаем метод объекта john в свойство объекта foo без вызова и следующей строкой вызываем его на нем. Контекстом в этот раз будет объект foo. Ошибки не будет только потому, что у объекта foo есть свойство fullName

Сценарий 3:
var bar = john.getName;
console.log(bar()); // undefined

В данном случае в стандартном режиме контекстом будет window, а в строгом режиме вылетит исключение:
Cannot read property 'firstName' of undefined
так как this в строгом режиме будет ссылаться на undefined

Когда вы передаете метод в колбек onClick или в любой другой колбек события, вызов идет подобно третьему сценарию. Поэтому вы должны позаботиться о том, чтобы ваш метод не терял контекст, используя один из способов приведенных выше.
Ответ написан
@vshvydky
Либо забинди хендлер либо стрелочную функцию, ты теряешь контекст
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
ITK academy Нижний Новгород
от 50 000 до 90 000 ₽
IT ATLAS Москва
от 200 000 до 250 000 ₽
ITK academy Казань
от 50 000 до 90 000 ₽