简体   繁体   English

将handleSubmit()传递给子组件不会修改父组件的状态

[英]Passing handleSubmit() to child component does not modify parent's state

I am new to React and Javascript. 我是React和Java的新手。

I am trying to have a user fill in a form that describes what a "Mob" should look like. 我正在尝试让用户填写一个描述“暴民”外观的表格。 When the user hits submit, I expect handleSubmit() (passed in through a parent) to modify the parent's state, which is an object. 当用户点击“提交”时,我希望handleSubmit() (通过父级传递)可以修改父级的状态,即对象。 However, this behavior is not happening. 但是,这种现象没有发生。

Here is the parent component, called App. 这是父组件,称为App。

class App extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            mob: new Mob("", "")
        };

        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(event) {
        event.preventDefault();
        alert("A name was submitted: " + this.state.vnum + " event value: " + event.state.vnum);
        const newMob = new Mob(event.state.vnum, event.state.shortDesc);

        this.setState({
            mob: newMob
        });
    }

    render() {
        return (
            <div>
                <MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />
                {console.log("parsed mob vnum: " + this.state.mob.vnum)}
            </div>
        );
    }
}

The child component, called MobForm 子组件,称为MobForm

class MobForm extends React.Component {
    render() {
        return (
            <div>
                <form onSubmit={this.props.onSubmit}>
                    <CreateStringInputField
                        name="vnum"
                        label="vnum:"
                    />
                    <CreateStringInputField
                        name="shortDesc"
                        label="Short Desc:"
                    />
                    <input type="submit" value="Submit" />
                </form>
                {console.log(this.state)}
            </div>
        );
    }
}

Which is calling CreateStringInputField() 哪个正在调用CreateStringInputField()

function CreateStringInputField(props) {
    return (
        <div name="row">
            <label>
                <b>{props.label}</b>
                <br />
                <input
                    type="text"
                    name={props.name}
                    label={props.label}
                />
            </label>
        </div>
    );
}

And, in case it matters, here is what "Mob" looks like. 而且,万一重要,这就是“暴民”的模样。

class Mob {
    constructor(vnum, shortDesc) {
        this.vnum = vnum;
        this.shortDesc = shortDesc;
    };
}

I expect to see {console.log("parsed mob vnum: " + this.state.mob.vnum)} print out the vnum as entered by a user. 我希望看到{console.log("parsed mob vnum: " + this.state.mob.vnum)}打印出用户输入的vnum。 Instead, I see nothing. 相反,我什么也没看到。 How can I achieve this expected output? 我怎样才能达到预期的输出?

In this line 在这条线

<MobForm mob={this.state.mob} onSubmit={() => this.handleSubmit} />

you are defining an anonymous function that returns your handleSubmit function. 您正在定义一个匿名函数,该函数返回handleSubmit函数。

In your form 以您的形式

<form onSubmit={this.props.onSubmit}>

onSubmit will execute the this.props.onSubmit which just returns the handleSubmit function but it wont execute it. onSubmit将执行this.props.onSubmit,该方法仅返回handleSubmit函数,但不会执行该函数。 To fix it just change MobForm to pass handleSubmit directly instead of passing it in an anonymous function: 要解决此问题,只需将MobForm更改为直接传递handleSubmit即可,而不是通过匿名函数传递它:

<MobForm mob={this.state.mob} onSubmit={this.handleSubmit} />

To handle the submission correctly you need to convert your form inputs to managed components. 为了正确处理提交,您需要将表单输入转换为托管组件。 See docs here 在这里查看文档

Something like this would be a good start: 这样的事情将是一个好的开始:

class MobForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            vnum: '',
            shortDesc: '',
        };

        this.handleChangeVnum = this.handleChangeVnum.bind(this);
        this.handleChangeShortDesc = this.handleChangeShortDesc.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChangeVnum(event) {
        this.setState({vnum: event.target.value});
    }

    handleChangeShortDesc(event) {
        this.setState({shortDesc: event.target.value});
    }

    handleSubmit(event) {
        this.props.onSubmit(this.state);
        event.preventDefault();
    }

    render() {
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <CreateStringInputField
                        name="vnum"
                        label="vnum:"
                        value={this.state.vnum}
                        onChange={this.handleChangeVnum}
                    />
                    <CreateStringInputField
                        name="shortDesc"
                        label="Short Desc:"
                        value={this.state.shortDesc}
                        onChange={this.handleChangeShortDesc}
                    />
                    <input type="submit" value="Submit" />
                </form>
                {console.log(this.state)}
            </div>
        );
    }
}

And update CreateStringInputField() 并更新CreateStringInputField()

function CreateStringInputField(props) {
    return (
        <div name="row">
            <label>
                <b>{props.label}</b>
                <br />
                <input
                    type="text"
                    name={props.name}
                    label={props.label}
                    value={props.value}
                    onChange={props.onChange}
                />
            </label>
        </div>
    );
}

With React you won't need to work with plain classes. 使用React,您将不需要使用普通类。 Instead, the class extends a provided React component ( Component or PureComponent ) or if you don't need state , then'll use plain functions that just return some JSX. 相反,该类extends了提供的React组件( ComponentPureComponent ), 或者如果您不需要state ,那么将使用仅返回一些JSX的普通函数。

Working example : https://codesandbox.io/s/simple-form-kdh3w 工作示例https : //codesandbox.io/s/simple-form-kdh3w


index.js index.js

import React from "react";
import { render } from "react-dom";
import MobForm from "./components/MobForm";

// simple function that returns "MobForm" and it gets rendered by ReactDOM
function App() {
  return <MobForm />;
}

// applies "App" to a <div id="root"></div> in the public/index.html file
render(<App />, document.getElementById("root"));

components/MobForm/index.js (stateful parent component) components / MobForm / index.js (有状态的父组件)

import React, { Component } from "react";
import Form from "../Form";

const initialState = {
  vnum: "",
  shortDesc: ""
};

// a stateful parent that manages child state
class MobForm extends Component {
  constructor(props) {
    super(props);
    this.state = initialState;

    // since the class fields are normal functions, they'll lose context
    // of "this" when called as a callback. therefore, they'll need 
    // to be bound to "this" -- via bind, "this" is now referring to 
    // the Class, instead of the global window's "this")
    this.handleChange = this.handleChange.bind(this);
    this.handleReset = this.handleReset.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  // a reusable class field that stores an input's value via its "name"
  // for example: [vnum]: "12345", [shortDesc]: "A number"
  // using object destructuring for shorter syntax:
  // [event.target.name]: event.target.value
  handleChange({ target: { name, value } }) {
    this.setState({ [name]: value });
  }

  // a class field to reset state
  handleReset() {
    this.setState(initialState);
  }

  // a class field to "submit" the form and alert what's currently in state
  handleSubmit(event) {
    // preventDefault prevents page refreshes
    event.preventDefault(); 

    // JSON.stringify allows you to print the contents of an object
    // otherwise, you'll just see [object Object]
    alert(JSON.stringify(this.state, null, 4)); 

    // clears state after submitting form
    this.handleReset(); 
  }

  render() {
    return (
      // passing down state via the spread operator, shorthand for 
      // "vnum={this.state.vum}" and "shortDesc={this.state.shortDesc}",
      // as well as, passing down the class fields from above
      <Form
        {...this.state}
        handleChange={this.handleChange}
        handleReset={this.handleReset}
        handleSubmit={this.handleSubmit}
      />
    );
  }
}

export default MobForm;

components/Form/index.js (a child function that returns some form JSX) components / Form / index.js (一个返回某种形式的JSX的子函数)

import React from "react";
import PropTypes from "prop-types";
import Input from "../Input";

// using object destructuring to pull out the MobForm's passed down
// state and fields. shorthand for using one parameter named "props" 
// and using dot notation: "props.handleChange", "props.handleReset", etc
function Form({ handleChange, handleReset, handleSubmit, shortDesc, vnum }) {
  return (
    <form style={{ width: 200, margin: "0 auto" }} onSubmit={handleSubmit}>
      <Input name="vnum" label="vnum:" value={vnum} onChange={handleChange} />
      <Input
        name="shortDesc"
        label="Short Desc:"
        value={shortDesc}
        onChange={handleChange}
      />
      <button type="button" onClick={handleReset}>
        Reset
      </button>{" "}
      <button type="submit">Submit</button>
    </form>
  );
}

// utilizing "PropTypes" to ensure that passed down props match 
// the definitions below
Form.propTypes = {
  handleChange: PropTypes.func.isRequired,
  handleReset: PropTypes.func.isRequired,
  handleSubmit: PropTypes.func.isRequired,
  shortDesc: PropTypes.string,
  vnum: PropTypes.string
};

export default Form;

components/Input/index.js (a reuseable input function) components / Input / index.js (可重用的输入函数)

import React from "react";
import PropTypes from "prop-types";

// once again, using object destructuring to pull out the Form's 
// passed down state and class fields. 
function Input({ label, name, value, onChange }) {
  return (
    <div name="row">
      <label>
        <b>{label}</b>
        <br />
        <input
          type="text"
          name={name}
          label={label}
          value={value}
          onChange={onChange}
        />
      </label>
    </div>
  );
}

// utilizing "PropTypes" to ensure that passed down props match 
// the definitions below
Input.propTypes = {
  label: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  value: PropTypes.string,
  onChange: PropTypes.func.isRequired
};

export default Input;

I was able to get my desired behavior by passing a function to MobForm which updates this.state.mob . 我可以通过将函数传递给MobForm来获得期望的行为,该函数会更新this.state.mob

App 应用

class App extends React.Component {
    state = {
        mob: new Mob("", "")
    };

    updateMob = newMob => {
        this.setState({
            mob: newMob
        });
    };

    render() {
        return (
            <div>
                <MobForm mob={this.state.mob} onSubmit={this.updateMob} />
            </div>
        );
    }
}

I then made MobForm maintain vnum, shortDesc state that I could use in my onChange() 然后,我使MobForm可以在onChange()使用vnum,shortDesc状态。

MobForm MobForm

    state = { vnum: "", shortDesc: "" };

    handleSubmit = event => {
        event.preventDefault();
        const mob = new Mob(this.state.vnum, this.state.shortDesc);
        this.props.onSubmit(mob);
    };

        render() {
        return (
            <div>
                <form onSubmit={this.handleSubmit}>
                    <CreateStringInputField
                        name="vnum"
                        value={this.state.vnum}
                        onChange={event => this.setState({ vnum: event.target.value })}
                    />
                    <CreateStringInputField
                        name="short desc"
                        value={this.state.shortDesc}
                        onChange={event => this.setState({ shortDesc: event.target.value })}
                    />
                    <input type="submit" value="Submit" />
                </form>
            </div>
        );
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM