Я следую учебнику для начинающих от Pluralsight, при отправке формы значение передается методу компонента addUser, и мне нужно отправить userName в this.state.users, но я получаю ошибку

 App.jsx:14 Uncaught TypeError: Cannot read property 'users' of undefined

Компонент

import React from 'react'
import User from 'user'
import Form from 'form'

class Component extends React.Component {
    constructor() {
        super()
        this.state = {
            users: null
        }
    }
    // This is triggered on form submit in different component
    addUser(userName) { 
        console.log(userName) // correctly gives String
        console.log(this.state) // this is undefined
        console.log(this.state.users) // this is the error
        // and so this code doesn't work
        /*this.setState({
            users: this.state.users.concat(userName)
        })*/
    }
    render() {
        return (
            <div>
            <Form addUser={this.addUser}/>
            </div>
            )
    }
}

export default Component
57
Ivan Topić 1 Сен 2017 в 13:57

9 ответов

Лучший ответ

Когда вы вызываете {this.addUser}, он вызывается, здесь this является экземпляром вашего класса (компонента), и поэтому он не дает вам ошибки, потому что метод addUser существует в вашем классе scope , но когда вы используете метод addUser, вы используете this для обновления state, которые существуют в область действия класса (компонента), но в настоящее время вы находитесь в области действия метода addUser и поэтому выдает ошибку, как в addUser области видимости, вы ничего не получаете, например, состояние, пользователь и т. д. Поэтому для решения этой проблемы вам нужно связать this во время вызова метода addUser. Так что ваш метод всегда знает экземпляр this.

Таким образом, последнее изменение в вашем коде будет выглядеть так:

<Form addUser={this.addUser.bind(this)}/>

< Сильный > ИЛИ


this DOM

Таким образом, вы можете сделать это следующим образом:

  constructor(props) {
    super(props);
    this.state = {
        users: null
    }
    this.addUser=this.addUser.bind(this);
}

И теперь вы можете называть это как обычно: -

<Form addUser={this.addUser}/>

Я надеюсь, что это сработает, И я дал вам понять.

96
Vinit Raj 30 Янв 2018 в 11:38

У меня была та же проблема, но моя проблема была в попытке получить доступ к this.state до завершения вызова this.state = { ... }. Делал что-то вроде этого this.state = { ...this.function1() } и function1 = () => { a: this.state.b }. Надеюсь, это поможет кому-то

2
Marc Sloth Eastman 18 Янв 2019 в 22:16

Подходы @Vinit Raj работают отлично - поэтому я предпочитаю использовать синтаксис функции стрелки, как это.

<Form addUser={ () => this.addUser() }/>

Используя такую анонимную функцию, вам не нужно никуда связывать ее.

13
Filtenborg 3 Сен 2018 в 12:19

Если вы предпочитаете использовать функцию стрелки, сделайте это. Синтаксис функции стрелки, как показано ниже

addUser = event => { 
    const { value }  = event.target;
    console.log("value", value);
}

Чтобы предотвратить вызов этой функции при каждом рендеринге или рендеринге, необходимо

+ Изменить

<Form addUser={this.addUser}/>

К

<Form addUser={() => this.addUser()}/>

Так что addUser вызывается только тогда, когда событие происходит / запускается

6
Hemadri Dasari 9 Ноя 2018 в 02:23

В вашем случае, объявив свою функцию как функцию жирной стрелки, вы добавляете контекст и удаляете требование связываться с этим. Это работает так же, как и другие решения, но значительно упрощает как запись, так и чтение. Просто поменяй ...

 addUser(userName) { 
    console.log(userName) // correctly gives String
    console.log(this.state) // this is undefined
    console.log(this.state.users) // this is the error
    // and so this code doesn't work
    /*this.setState({
        users: this.state.users.concat(userName)
    })*/
}

Чтобы ...

addUser = (userName) => { 
    console.log(userName) // correctly gives String
    console.log(this.state) // this is undefined
    console.log(this.state.users) // this is the error
    // and so this code doesn't work
    /*this.setState({
        users: this.state.users.concat(userName)
    })*/
}

А все остальное может остаться прежним.

1
user6506514user6506514 25 Янв 2019 в 17:23

Я не думаю, что другие ответы объясняют это очень хорошо. По сути, проблема в том, что ключевое слово Javascript this является безумным (MDN очень щедро говорит, что оно " в JavaScript ведет себя немного иначе, чем в других языках ").

TL; DR состоит в том, что this не обязательно является классом, в котором определен метод. Когда вы используете this.addUser в своем элементе <Form>, this фактически является {{ X4}} объект! Я не уверен, почему React делает это - я не могу понять, почему кто-то этого захочет, но это легко проверить. Просто поместите console.log(this) в addUser и вы обнаружите, что это экземпляр Form.

В любом случае, решение довольно простое - используйте функцию стрелки. Функции стрелок делают копию this в том месте, где они определены. Так что, если вы поместите один в вашу функцию рендеринга, как и другие люди предложили:

<Form addUser={() => this.addUser()}/>

Затем, когда render() запущен, this будет ссылаться на объект Component, функция стрелки создаст его копию, а затем при запуске она будет использовать эту копию. Это в основном сокращение для этого кода (и это то, что люди делали до функций стрелок):

    render() {
        const that = this; // Make a copy of this.
        return (
            <div>
            <Form addUser={function() { return that.addUser; }}/>
            </div>
            )
    }

Кроме того, как уже упоминали другие люди, создание новой функции при каждом вызове функции рендеринга может повлиять на производительность, поэтому лучше захватывать this один раз где-нибудь еще. Я думаю, что это лучший подход, чем то, что предложили другие люди:

class Component extends React.Component {
    constructor() {
        super()
        this.state = {
            users: null
        }
    }
    // This is triggered on form submit in different component
    addUser = (userName) => { 
        this.setState({
            users: this.state.users.concat(userName)
        })
    }
    render() {
        return (
            <div>
            <Form addUser={this.addUser}/>
            </div>
            )
    }
}

Но это мой первый день, когда я использую React, и я обычно стараюсь избегать использования языков, столь же лающих, как Javascript, так что принимайте все это с небольшим количеством соли!

0
Timmmm 24 Янв 2020 в 11:40

Проблема в этом контексте, поэтому используйте

<form onSubmit={(event)=>this.addUser(event)}>
   // Inputs
</form>
addUser(event){
   event.preventDefault()
   // Code this.state 
}
0
Gil 12 Июл 2020 в 15:41

Хорошим примером является привязка метода к классу в функции конструктора. Видеть https://reactjs.org/docs/handling-events.html

import React from 'react'
import User from 'user'
import Form from 'form'

class Component extends React.Component {
    constructor() {
        super()
        this.state = {
            users: null
        }
  this.addUser = this.addUser.bind(this); 
  //bind functions which need access to "this"v in the constructor here. 

    }
    // This is triggered on form submit in different component
    addUser(userName) { 
        console.log(userName) // correctly gives String
        console.log(this.state) // this is undefined
        console.log(this.state.users) // this is the error
        // and so this code doesn't work
        /*this.setState({
            users: this.state.users.concat(userName)
        })*/
    }
    render() {
        return (
            <div>
            <Form addUser={this.addUser}/>
            </div>
            )
    }
}

export default Component
0
Greggory Wiley 4 Мар 2020 в 03:41

Просто используйте async в функции

addUser =async (userName) => { console.log(this.state.users) }

Это будет работать нормально

-1
Qamar 11 Сен 2019 в 15:19