React - TypeError:无法读取undefined的属性'props'

时间:2018-06-14 16:35:51

标签: javascript reactjs this bind react-props

我正在尝试创建一个能够删除列表中项目的点击事件,但是当我点击它时,我得到“TypeError:无法读取未定义的属性'道具'。”

我试图尽可能地坚持使用ES6,而且我很确定它可以在某个地方绑定“这个”,但我已经尝试了很多地方并且没有成功。

import React, { Component } from 'react';
import './App.css';

class App extends Component {
    render() {
        return (
            <div className="App">
                <StreetFighter />
            </div>
        );
    }
}

class StreetFighter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            characters: [
                'Chun-Li',
                'Guile',
                'Ryu',
                'Ken',
                'E.Honda',
                'Dhalsim',
            ],
        };
    }
    render() {
        let characters = this.state.characters;
        characters = characters.map((char, index) => {
            return (
                <Character char={char} key={index} onDelete={this.onDelete} />
            );
        });
        return (
            <div>
                <p>Street Fighter Characters</p>
                <ul>{characters}</ul>
            </div>
        );
    }
    onDelete(chosenCharacter) {
        let updatedCharactersList = this.state.characters.filter(
            (char, index) => {
                return chosenCharacter !== char;
            }
        );

        this.setState({
            characters: updatedCharactersList,
        });
    }
}

class Character extends Component {
    render() {
        return (
            <li>
                <div className="character">
                    <span className="character-name">{this.props.char}</span>
                    <span
                        className="character-delete"
                        onClick={this.handleDelete}
                        > x </span>
                </div>
            </li>
        )
    };

    handleDelete() {
        this.props.onDelete(this.props.char);
    }
}


export default App;

5 个答案:

答案 0 :(得分:6)

由于JS OOP系统将类传递给像这样的道具时,你重写了类方法的上下文。因此,为了使其工作,有几种方法:

1)这不太好,因为bind alwaus会返回新函数,即使没有更新道具,你的组件也会重新呈现

import React, { Component } from 'react';
import './App.css';

class App extends Component {
    render() {
        return (
            <div className="App">
                <StreetFighter />
            </div>
        );
    }
}

class StreetFighter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            characters: [
                'Chun-Li',
                'Guile',
                'Ryu',
                'Ken',
                'E.Honda',
                'Dhalsim',
            ],
        };
    }
    render() {
        let characters = this.state.characters;
        characters = characters.map((char, index) => {
            return (
                <Character char={char} key={index} onDelete={this.onDelete.bind(this)} />
            );
        });
        return (
            <div>
                <p>Street Fighter Characters</p>
                <ul>{characters}</ul>
            </div>
        );
    }
    onDelete(chosenCharacter) {
        let updatedCharactersList = this.state.characters.filter(
            (char, index) => {
                return chosenCharacter !== char;
            }
        );

        this.setState({
            characters: updatedCharactersList,
        });
    }
}

class Character extends Component {
    render() {
        return (
            <li>
                <div className="character">
                    <span className="character-name">{this.props.char}</span>
                    <span
                        className="character-delete"
                        onClick={this.handleDelete.bind(this)}
                        > x </span>
                </div>
            </li>
        )
    };

    handleDelete() {
        this.props.onDelete(this.props.char);
    }
}


export default App;

2)在我的代码中,我使用箭头函数作为此类情况的类属性(这是我认为最常见的解决方案之一)

class Character extends Component {
    render() {
        return (
            <li>
                <div className="character">
                    <span className="character-name">{this.props.char}</span>
                    <span
                        className="character-delete"
                        onClick={this.handleDelete.bind(this)}
                        > x </span>
                </div>
            </li>
        )
    };

    handleDelete = () => {
        this.props.onDelete(this.props.char);
    }
}

答案 1 :(得分:6)

TLDR:您的鳕鱼中的具体问题在本答案末尾的段落中说明。

这是JavaScript的this的经典问题,如果你还没有,我建议你稍微阅读一下。

简而言之(不仅适合你,但如果其他人正在阅读本文),JavaScript函数定义(如果不是arrow function)重新定义this是什么,即它指向的是什么。 所以当你定义:

handleDelete() {
     this.props.onDelete(this.props.char);
}

该函数的this并未指向它所定义的类的对象实例。如果您来自C ++ / C#/ Java背景,这有点违反直觉。问题是this在类进入JavaScript之前就已经退出了,而且对于带有一堆定义原型的函数来说类不仅仅是语法糖(参见here),或者换句话说它不是默认情况下将其绑定到其函数。

有几种典型的解决方法:

this绑定到所有函数(在构造函数中)

class Character extends Component {
    constructor(props) {
        super(props)
        this.handleDelete = this.handleDelete.bind(this)
    }
    render() {
        // ...
    };

    handleDelete() {
        this.props.onDelete(this.props.char);
    }
}

注意:而不是这个你可以在每次使用函数时绑定this (即onClick={this.handleDelete.bind(this)},但是不是建议,因为如果你忘记绑定this,它会使你的代码容易出错。如果你是链接函数,你可能会在某处指出错误的东西。更不用说{ {1}}是一个函数,在React中,你将在每个渲染上进行函数调用。但是,如果你遇到需要更改的情况,请记住是一件好事。 {1}}

使用箭头功能

bind

如上所述,在其他答案中,箭头函数不会重新定义this指针。你在这里有效地做的是将箭头函数分配给该类的对象实例的属性。换句话说,函数(是一个不重新定义class Character extends Component { render() { // ... }; handleDelete = () => { this.props.onDelete(this.props.char); } } 的箭头函数)从外部作用域(类的作用域)中获取this,但是,因为箭头函数是匿名函数,所以你命名它通过将其分配给name property

所有其他解决方案都是上述两种解决方案的一些变体

关于您的解决方案

thisthis都遇到此onDelete问题。

此外,正如@Alyson Maia所述,您的handleDelete组件可以写为functional component

this

答案 2 :(得分:1)

通过使用箭头功能,您可以解决this上下文。试试这个:

你的onClick事件 onClick={this.handleDelete}

和你的功能定义:

handleDelete = () => {
    //here you can access the this.props
}

答案 3 :(得分:1)

当您在this内使用handleDelete时,您没有引用该类。您可以使用followwing approches解决此问题

使用无状态组件(在您的情况下最好的方法)

不改变状态的组件,不需要Class,可以将它们定义为函数或常量

Class Parent extends React.Component {
  state = { ... }

  onDelete = () => { ... }

  render() {
    return (
      <Child onDelete={this.onDelete} />
    )
  }
}

function Child(props) {
  return (
    <button onClick={props.onDelete}>Delete</button>
  )
}

使用箭头功能

箭头函数不会在类范围内的箭头函数内定义范围。

Class Parent extends React.Component {
  state = { foo: 'bar' }

  wrongMethod() {
    console.log(this.state) // undefined
  }

  rightMethod = () => {
    console.log(this.state) // { foo: 'bar' }
  }

  render() {
    this.wrongMethod()
    this.rightMethod()
    return (
      <h1>Hello World!</h1>
    )
  }
}

使用bind

如果你有一个使用this的方法,你必须将方法范围绑定到类范围,这可以像下面这样做。由于每次渲染都会调用bindOnRender并在每次调用时创建一个新函数,因此Class Parent extends React.Component { constructor() { this.state = { foo: 'bar' } this.bindOnConstructor.bind(this) } bindOnConstructor() { console.log(this.state) // { foo: 'bar' } } bindOnRender = () => { console.log(this.state) // { foo: 'bar' } } render() { return ( <button onClick={this.bindOnConstructor}>Foo</button> <button onClick={this.bindOnRender.bind(this)}>Bar</button> ) } } 会出现性能问题。

nyc.domain.com          => domain.com/daughter.php?d=nyc
washington.domain.com   => domain.com/daughter.php?d=washington
...

答案 4 :(得分:1)

创建用于处理事件的函数时,请不要忘记通过构造函数将其添加到道具中,如下所示:

constructor (props) {
  super(props)
  this.yourFunction = this.yourFunction.bind(this)
}