Home > Blockchain >  Keeping track of and incrementing states based off of values within child components [React]
Keeping track of and incrementing states based off of values within child components [React]

Time:06-16

I'm using a form and two spans to increment/decrement multiple inputs. Each time the form is modified (determined thru onclick), I want to update the sum accordingly. I tried implementing the setState function correctly so as to account for React's state call batching; but it doesn't seem to help (if you run this with console.log, you'll notice that the information is a step behind). I believe it stems from trying to get the value of inputField (top of onClick function & very scuffed implementation). However, I'm not sure how to get and increment this information otherwise. I'm relatively new to React, so even some insight on how I should be structuring this would be helpful (as this poor design seems to be the root of my issues).

- Thanks in advance

import React, { Component, PureComponent } from "react";

function App() {
  return (
    <div className="App">
      <Parent />
    </div>
  );
}

class Parent extends PureComponent {
  state = {
    formState: {
      // list all objectives here
      firstObjective: [0, 10],
      secondObjective: [0, 30],
      thirdObjective: [0, 1],
    },
    runningTotal: 0,
  };

  onClick = event => {
    var inputField = event.target;
    while (inputField.tagName !== 'INPUT') {
      inputField = inputField.nextElementSibling;
    }
    this.setState((prevState) => {
      return {
        formState: {
          ...prevState.formState,
          [inputField.name]: [
            (event.target.className === 'arrow down') ?
              Number(Math.max(inputField.value - 1, 0)) :
              Number(inputField.value)   1,
            Number(inputField.className)
          ]
        }
      }
    });

    // ref all objectives here
    var objectives = [
      this.state.formState.firstObjective,
      this.state.formState.secondObjective,
      this.state.formState.thirdObjective
    ];
    console.log(objectives);

    var sum = 0;
    for (let i = 0; i < objectives.length; i  ) {
      sum  = Math.max(Number(objectives[i][0]) - 1, 0) * Number(objectives[i][1]);
    }
    this.setState({
      runningTotal: sum
    });
  }

  render() {
    const { formState } = this.state;
    return (
      <div>
        <Form
          formState={formState}
          onClick={this.onClick}
          runningTotal={this.state.runningTotal}
        />
      </div>
    );
  }
}

class Field extends Component {
  state = { inputFieldValue: 0, }

  increment(event) {
    this.props.onClick(event, this.inputFieldValue);
  }

  render() {
    return (
      <div>
        <div className="field">
          <label htmlFor={this.props.name}>{this.props.label}</label>
          <div className="numInput">
            <span className="arrow up" onClick={this.props.onClick} />
            <span className="arrow down" onClick={this.props.onClick} />
            <input type="text" name={this.props.name} value={this.props.value} className={this.props.className} readOnly />
          </div>
        </div>
      </div>
    );
  }
}

class Form extends Component {
  render() {
    return (

      <>
        <form className="form">
          <fieldset>
            <Field
              name="firstObjective" // must match firstObjective
              label="First Objective"
              value={this.props.formState.firstObjective[0]}
              className={this.props.formState.firstObjective[1]}
              onClick={this.props.onClick} />
            <Field
              name="secondObjective" // must match secondObjective
              label="Second Objective"
              value={this.props.formState.secondObjective[0]}
              className={this.props.formState.secondObjective[1]}
              onClick={this.props.onClick} />
            <Field
              name="thirdObjective" // must match third objective
              label="Third Objective"
              value={this.props.formState.thirdObjective[0]}
              className={this.props.formState.thirdObjective[1]}
              onClick={this.props.onClick} />
          </fieldset>
        </form>
        <div className="total">Total: {this.props.runningTotal}</div>
      </>
    );
  }
};

export default App;

CodePudding user response:

You need not keep runningTotal in your state because it's calculated from other state values.

You can define a function in Parent component to calculate the runningTotal

const getRunningTotal = () => {
    const objectives = [
        this.state.formState.firstObjective,
        this.state.formState.secondObjective,
        this.state.formState.thirdObjective,
    ];

    let sum = 0;
    for (let i = 0; i < objectives.length; i  ) {
        sum  = Math.max(Number(objectives[i][0]) - 1, 0) * Number(objectives[i][1]);
    }

    return sum;
};

And then use it to provide prop for Form component

<Form
     formState={formState}
     onClick={this.onClick}
     runningTotal={this.getRunningTotal()}
/>
  • Related