0

I have trouble with simple task of adding elements selected in checkboxes to an array in component state. It seems like the push method for state.toppings (Editor.js) is invoked twice for each checkbox click, even though console.log shows that updateFormValueCheck method is invoked once per click. Can anyone help?

This is App.js

import React, { Component } from "react";
import { Editor } from "./Editor";
import { Display } from "./Display";
export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      formData: {}
    }
  }
  submitData = (newData) => {
    console.log("newData", newData)
    this.setState({ formData: newData });
  }
  render() {
    return <div className="container-fluid">
      <div className="row p-2">
        <div className="col-6">
          <Editor submit={this.submitData} />
        </div>
        <div className="col-6">
          <Display data={this.state.formData} />
        </div>
      </div>
    </div>
  }
}

This is Editor.js

import React, { Component } from "react";
export class Editor extends Component {
    constructor(props) {
        super(props);
        this.state = {
            toppings: ["Strawberries"]
        }
        this.toppings = ["Sprinkles", "Fudge Sauce",
            "Strawberries", "Maple Syrup"]
    }

    updateFormValueCheck = (event) => {
        event.persist();
        this.setState(state => {
            if (event.target.checked) {
                state.toppings.push(event.target.name);
            } else {
                let index = state.toppings.indexOf(event.target.name);
                state.toppings.splice(index, 1);
            }
        }, () => this.props.submit(this.state));
    }
    render() {
        return <div className="h5 bg-info text-white p-2">
            <div className="form-group">
                <label>Ice Cream Toppings</label>
                {this.toppings.map(top =>
                    <div className="form-check" key={top}>
                        <input className="form-check-input"
                            type="checkbox" name={top}
                            value={this.state[top]}
                            checked={this.state.toppings.indexOf(top) > -1}
                            onChange={this.updateFormValueCheck} />
                        <label className="form-check-label">{top}</label>
                    </div>
                )}
            </div>
        </div>
    }
}

This is Display.js

import React, { Component } from "react";

export class Display extends Component {
    formatValue = (data) => Array.isArray(data)
        ? data.join(", ") : data.toString();
    render() {
        let keys = Object.keys(this.props.data);
        if (keys.length === 0) {
            return <div className="h5 bg-secondary p-2 text-white">
                No Data
            </div>
        } else {
            return <div className="container-fluid bg-secondary p-2">
                {keys.map(key =>
                    <div key={key} className="row h5 text-white">
                        <div className="col">{key}:</div>
                        <div className="col">
                            {this.formatValue(this.props.data[key])}
                        </div>
                    </div>
                )}
            </div>
        }
    }
}

The output is: enter image description here

0

1 Answer 1

1

You cannot directly mutate this.state, it can only be done using this.setState. For more info. refer this: Why can't I directly modify a component's state, really?

Therefore, you need to update your Editor component as follows.

componentDidMount is used to display the initial state during the initial rendering. Then componentDidUpdate is used to render the state changes through display component whenever it's updated.

import React, { Component } from "react";
export class Editor extends Component {
  constructor(props) {
    super(props);
    this.state = {
      toppings: ["Strawberries"],
    };
    this.toppings = ["Sprinkles", "Fudge Sauce", "Strawberries", "Maple Syrup"];
  }

  updateFormValueCheck = (event) => {
    event.persist();

    let data;

    if (event.target.checked) {
      data = [...this.state.toppings, event.target.name];
    } else {
      const index = this.state.toppings.indexOf(event.target.name);
      const temp = [...this.state.toppings];
      temp.splice(index, 1);
      data = temp;
    }

    this.setState({
      toppings: data,
    });
  };

  componentDidMount() {
    this.props.submit(this.state.toppings);
  }

  componentDidUpdate(prevPros, prevState) {
    if (prevState.toppings !== this.state.toppings) {
      this.props.submit(this.state.toppings);
    }
  }

  render() {
    console.log(this.state);
    return (
      <div className="h5 bg-info text-white p-2">
        <div className="form-group">
          <label>Ice Cream Toppings</label>
          {this.toppings.map((top) => (
            <div className="form-check" key={top}>
              <input
                className="form-check-input"
                type="checkbox"
                name={top}
                value={this.state[top]}
                checked={this.state.toppings.indexOf(top) > -1}
                onChange={this.updateFormValueCheck}
              />
              <label className="form-check-label">{top}</label>
            </div>
          ))}
        </div>
      </div>
    );
  }
}

Hope this would be helpful to solve your issue.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.