对React Callback表单的困惑

时间:2019-10-09 06:25:03

标签: javascript reactjs

因此,我正在尝试学习与Rails一起反应(仅将Rails用作API)。我制作了一个简单的待办事项应用程序,尝试“创建”列表时陷入困境。

我在这里显示了一个“新列表”组件,该组件主要来自于React Forms教程:

import React, { Component } from 'react';
import axios from 'axios';

class ListForm extends Component {
    constructor(props) {
      super(props);
      this.state = {
        title: '',
        description: ''
      };
      this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(e) {
      this.setState({[e.target.name]: e.target.value});
    }

    handleSubmit(e) {
      console.log("Form submitted with: " + this.state.value)
      e.preventDefault();
    }

    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            Title:
            <input name="title" type="text" value={this.state.value} onChange={this.handleInputChange} />
          </label>
          <label>
            Description:
            <textarea name="description" value={this.state.value} onChange={this.handleInputChange} />
          </label>
          <input type="submit" value="Submit" />
        </form>
      );
    }
  }

  export default ListForm;

我的ListContainer显示在这里

import React, { Component } from 'react';
import axios from 'axios';
import List from './List';
import ListForm from './ListForm'

class ListContainer extends Component {
    constructor(props){
        super(props)
        this.state = {
            lists: []
        }

        this.addNewList = this.addNewList.bind(this)
    }


    componentDidMount() {
        axios.get('/api/v1/lists.json')
        .then(response => {
            console.log(response)
            this.setState({
                lists: response.data
            })
        })
        .catch(error => console.log(error))
    }

    addNewList(title, description) {
        axios.post('/api/v1/lists.json', {
            title: title,
            description: description
          })
          .then(function (response) {
            console.log(response);
            const lists = [ ...this.state.lists, response.data ]
            console.log(...this.state.lists)
            this.setState({lists})
          })
          .catch(function (error) {
            console.log(error);
          });
    }

    render() {
        return (
            <div className="lists-container">
                {this.state.lists.map( list => {
                    return (<List list={list} key={list.id} />)
                })}
                <ListForm onSubmit={this.addNewList} />
            </div>
        )
    }
}

export default ListContainer;

我的问题来自对提交回调的误解。我知道当我在表单上执行“ onSubmit”时,它使用addNewList函数作为回调....但是我真的不明白,ListForm中的状态连接如何进入该回调函数。我显然在做错事,因为它不起作用,并且当前控制台显示“ Form with with:undefined”,因此它根本无法正确传递参数。

我对React还是很陌生的,并且对JS非常生锈(自从我使用它以来,已经有点了,所以我确定这里有一些新手错误)。同样,axios基本上是一种“更好的”获取方式。

我也不会说谎,我也不完全理解为什么我们要this.handleSubmit = this.handleSubmit.bind(this);(以及其他类似的事情)

3 个答案:

答案 0 :(得分:1)

我想你也有错字。对于变更事件,您有

<input name="title" type="text" value={this.state.value} onChange={this.handleInputChange} />

因此,更改的回调为this.handleInputChange。但是在您的代码中,其名为handleChange

但是,即使您使用了正确的命名,它也不起作用,因为您还需要绑定该函数。

这使我想到了关于this.handleSubmit = this.handleSubmit.bind(this);

的问题

这里的问题是,当您将函数作为回调传递时,它将丢失其上下文。考虑以下

const x = { 
  log: function() { console.log(this.val) },
  val: 10
}

现在您可以做

x.log(); // -> print 10

但是当你这样做

y = x.log;
y(); // -> prints undefined

如果仅传递周围的函数,则会失去其上下文。要解决此问题,您可以bind

x.log = x.log.bind(x);
y = x.log
y(); // -> prints 10

希望这很有意义:)

无论如何,回到您的问题上,您不必使用绑定,有更好的方法

class ListForm extends Component {
constructor(props) {
  super(props);
  this.state = {
    title: '',
    description: ''
  };
}

handleChange = (e) => {
  this.setState({[e.target.name]: e.target.value});
}

handleSubmit = (e) => {
  console.log("Form submitted with: " + this.state.value)
  e.preventDefault();
}

虽然没有经过测试,但现在可能会正常工作!

答案 1 :(得分:1)

您是如此亲密!我们只需要进行一些调整。

首先让我们丢失bind语句,仅使用箭头函数。箭头函数没有this对象,因此,如果在该函数内调用this,则实际上将访问类实例的this对象。整洁。

第二,让我们修复handleChange函数上的错字,以便您的输入正确更新组件状态。

现在,真正解决您的问题。您需要在父组件ListContainer中调用addNewList函数。我们该怎么做?让它作为道具传递给子组件!您快到了,但是不要使用关键字onSubmit={this.addNewList},而要使用类似handleSubmit的名称。这是因为onSubmit实际上是一个特殊的关键字,它将事件侦听器附加到要提交的子组件上,而我们不希望这样做。

现在,您的子组件正在充当道具。我们可以在handleSubmit函数中调用它。然后,我们传入参数,标题和描述。现在,您的子组件可以在父组件中调用addNewList函数!

import React, { Component } from 'react';
import axios from 'axios';
import List from './List';
import ListForm from './ListForm'

class ListContainer extends Component {
    constructor(props) {
        super(props)
        this.state = {
            lists: []
        }

        this.addNewList = this.addNewList.bind(this)
    }


    componentDidMount() {
        axios.get('/api/v1/lists.json')
            .then(response => {
                console.log(response)
                this.setState({
                    lists: response.data
                })
            })
            .catch(error => console.log(error))
    }

    addNewList(title, description) {
        axios.post('/api/v1/lists.json', {
            title: title,
            description: description
        })
            .then(function (response) {
                console.log(response);
                const lists = [...this.state.lists, response.data]
                console.log(...this.state.lists)
                this.setState({ lists })
            })
            .catch(function (error) {
                console.log(error);
            });
    }

    render() {
        return (
            <div className="lists-container">
                {this.state.lists.map(list => {
                    return (<List list={list} key={list.id} />)
                })}
                <ListForm handleSubmit={this.addNewList} />
            </div>
        )
    }
}

export default ListContainer;
import React, { Component } from 'react';
import axios from 'axios';

class ListForm extends Component {
    constructor(props) {
        super(props);
        this.state = {
            title: '',
            description: ''
        };
    }

    handleChange = (e) => {
        this.setState({ [e.target.name]: e.target.value });
    }

    handleSubmit = (e) => {
        this.props.handleSubmit(this.state.title, this.state.description);
        e.preventDefault();
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    Title:
            <input name="title" type="text" value={this.state.value} onChange={this.handleChange} />
                </label>
                <label>
                    Description:
            <textarea name="description" value={this.state.value} onChange={this.handleChange} />
                </label>
                <input type="submit" value="Submit" />
            </form>
        );

答案 2 :(得分:0)

使用this.state.titlethis.state.description更改输入的值属性:

<input name="title" type="text" value={this.state.title} onChange={this.handleInputChange} />
<textarea name="description" value={this.state.description} onChange={this.handleInputChange} />

尝试使用

打印信息
console.log("Form submitted with: ", this.state)

关于.bind(this)

  • 发生任何事件时,都会将一个事件对象分配给回调函数。因此,回调函数或事件处理程序将失去对类的引用,this指向已调用的事件。
  • Bind创建一个新函数,该函数会将其设置为传递给bind()的第一个参数
  • .bind(this)箭头功能外,它还用于this。但是对于长层次结构而言,这不是可取的。