简体   繁体   中英

Clearing state and input values between parent and child components in React

Two part question here: First, can anyone explain to me why this.state.taskName and this.state.taskBody and their corresponding inputs aren't clearing after submitting the form? In handleSubmit() I'm using this.setState() to set their states to an empty string, but it doesn't seem to be working. It also wont let me submit more than once, which I suspect might have to do with the state not clearing.

Second, what would be the best way to push a task with multiple key-value pairs into the this.state.tasks array? I tried storing taskName and taskBody as an object in state, and also tried pushing the them into an object and then displaying them, but couldn't get it to work.

Here are parent, child, & sibling files:

import React, { Component } from 'react';
import Task from './Task/Task';
import NewTaskForm from './NewTaskForm/NewTaskForm';

class Board extends Component {
    constructor(props) {
        super(props);

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

        this.state = {
            tasks: [],
            taskName: '',
            taskBody: ''
        };
    }

    handleSubmit(e) {
        e.preventDefault();
        let updatedTasks = this.state.tasks;
        let taskName = this.state.taskName;
        let taskBody = this.state.taskBody;

        updatedTasks.push(taskName, taskBody);
        let updatedName = '';
        let updatedBody = '';
        this.setState({ tasks: updatedTasks, taskName: updatedName, taskBody: updatedBody });
    };

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

    render() {
        return (
            <div>
                <NewTaskForm
                    onSubmit={this.handleSubmit}
                    onChange={this.handleChange}
                />
                <Task
                    tasks={this.state.tasks}
                />
            </div>
        );
    }
}

export default Board;


import React, { Component } from 'react';

class NewTaskForm extends Component {
    constructor(props) {
        super(props);

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

    handleChange(e) {
        this.props.onChange(e.target);
    }

    render() {
        return (
            <form onSubmit={this.props.onSubmit}>
                <label>Task Name</label>
                <input
                    name="taskName"
                    required type="text"
                    value={this.props.taskName}
                    onChange={this.handleChange}
                    placeholder="Enter a task name"
                />

                <label>Task Body</label>
                <input
                    name="taskBody"
                    required type="text"
                    value={this.props.taskBody}
                    onChange={this.handleChange}
                    placeholder="Enter a task body"
                />

                <button
                    type="submit"
                    className="btn btn-default"
                >Add Task
                </button>
            </form>
        );
    }
}

export default NewTaskForm;


import React, { Component } from 'react';

class Task extends Component {
    render() {
        let taskList = this.props.tasks.map((task, i) => {
            return (
                <li key={i}>
                    {task}
                </li>
            );
        });
        return (
            <ul>
                {taskList}
            </ul>
        )
    }
}

export default Task;

Thanks!

To address your first question, the reason the inputs aren't clearing is because you are not passing the taskName and taskBody values as props to <NewTaskForm /> . The inputs aren't being controlled by React since NewTaskForm isn't receiving them, so they are currently entirely user-controlled. Add them and you'll see them clear after submitting the form.

The best way to hold a taskName / taskBody pair in state is as you said: an object. In your TaskComponent you'll need to change the mapping logic to work with an object, though, as well as make sure you push an object to this.state.tasks in Board . I've linked to a Fiddle that shows the changes I made: https://jsfiddle.net/0z89Lcpw/ .

Specifically the changes I made versus your code are:

  • modified line 21 to push an object with shape {taskName, taskBody}
  • added lines 37 and 38 to pass taskName and taskBody props to NewTaskForm
  • changed line 95 (old: line 93) to pull taskName and taskBody off of each task and present both--of course you can present these pieces of data in quite a few different ways to suit your presentational purposes.

I can see couple of issues with the way you have written the code. For starters, you are not passing in taskName and taskBody as props to NewTaskForm , as the component expects the value to be read from the props.

  • Not a good idea to mutate the state
  • As name and the body encompasses into a task, maintain a shape for it.

Check this code snippet - https://codesandbox.io/s/ov675m6r7y

Please see your altered code below. Ive added explanations beneath for the main alterations I've made :)

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

    this.state = {
      tasks: [],
      taskName: '',
      taskBody: ''
    };
  }

  handleSubmit(e) {
    e.preventDefault();

    let tasks = this.state.tasks;
    let taskName = this.state.taskName;
    let taskBody = this.state.taskBody;

    tasks.push({taskName, taskBody});

    this.setState({tasks, taskName: '', taskBody: ''});
  };

  handleChange(e) {
    const name = e.target.name; 
    const value = e.target.value;

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

  render() {
    return (
      <div>
        <NewTaskForm
          taskName={this.state.taskName}
          taskBody={this.state.taskBody}
          onSubmit={(e) => this.handleSubmit(e)}
          onChange={(e) => this.handleChange(e)}
        />
        <Tasks
          tasks={this.state.tasks}
        />
      </div>
    );
  }
}

class NewTaskForm extends React.Component {
  render() {
    return (
      <form onSubmit={this.props.onSubmit}>
        <label>Task Name</label>
        <input
          name="taskName"
          required type="text"
          value={this.props.taskName}
          onChange={(e) => this.props.onChange(e)}
          placeholder="Enter a task name"
        />

        <label>Task Body</label>
        <input
          name="taskBody"
          required type="text"
          value={this.props.taskBody}
          onChange={(e) => this.props.onChange(e)}
          placeholder="Enter a task body"
        />

        <button type="submit" className="btn btn-default">Add Task</button>
      </form>
    );
  }
}

class Tasks extends React.Component {
  render() {
    let taskList = this.props.tasks.map((task, i) => {
      return (
        <li key={i}>
          <b>{task.taskName}</b><br/>
          {task.taskBody}
        </li>
      );
    });

    return (
      <ul>
        {taskList}
      </ul>
    )
  }
}
  1. Passed through taskName and taskBody as props to your NewTaskForm component to use in their inputs.
  2. You were pushing the new task to your updated task list incorrectly.
  3. In your Task component you were not showing the properties of the task, you were attempting to display the task object.

Working fiddle: https://jsfiddle.net/8sLw4phf/2/

I would try something like this:

handleSubmit(e) {
    e.preventDefault();
    const { taskName, taskBody } = this.state;
    this.setState({ 
        tasks: [...this.state.tasks, { taskName, taskBody }] 
        taskName: '', 
        taskBody: '' 
    });
};

This way you are not mutating your state and your array contains one object per task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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