javascriptreactjsmultiple-input

Input onChange makes multiple re-renders


I learn React and I faced some problems. I thought I understand controlled components, but well, it seems otherwise. Could you explain me, why after onChange event I receive props for rest Input components? If I want add label that depends on this.pops.name it gets messy (because of props I see in console.log). I would be grateful for explanation.

import React, { Component } from "react";

class Input extends Component {
  handleChange = (e) => {
    this.props.onInputChange(e);
  };
  chooseLabel = (props) => {
    let { name } = props;
    console.log(name);
    if (name === "cost") {
      return (
        <label className="label" htmlFor={name}>
          How much do you earn per hour?
        </label>
      );
    } else if (name === "salary") {
      return (
        <label className="label" htmlFor={name}>
          How much do you want to spend?
        </label>
      );
    } else {
      return null;
    }
  };
  render() {
    console.log("this.props.nevVal", this.props.newVal);
    console.log("this.props.name", this.props.name);
    return (
      <div>
        {this.chooseLabel(this.props)}
        <input
          className="Input"
          value={this.props.newVal}
          name={this.props.name}
          placeholder={this.props.name}
          onChange={this.handleChange}
        />
      </div>
    );
  }
}

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      salary: "",
      cost: "",
      tip: ""
    };
  }

  handleInputChange = (e) => {
    console.log("E.target.name", e.target.name);
    this.setState({ [e.target.name]: e.target.value });
  };
  render() {
    return (
      <div className="App">
        <Input
          name="salary"
          newVal={this.state.salary}
          onInputChange={this.handleInputChange}
        />
        <Input
          name="cost"
          newVal={this.state.cost}
          onInputChange={this.handleInputChange}
        />
        <Input
          name="tip"
          newVal={this.state.tip}
          onInputChange={this.handleInputChange}
        />
      </div>
    );
  }
}

Link to Codesandbox.io


Solution

  • When one of the inputs is changed, you update the state of your App component. And when a state of a component is updated, it re-renders, and all its children too. So all the inputs are re-rendered (and their newVal are logged to the console), even if you change the value of a single input.

    I tried to illustrate it in the following snippet :

    class Input extends React.Component {
      handleChange = (e) => {
        this.props.onInputChange(e);
      };
      chooseLabel = (props) => {
        let { name } = props;
        // console.log(name);
        if (name === "cost") {
          return (
            <label className="label" htmlFor={name}>
              How much do you earn per hour?
            </label>
          );
        } else if (name === "salary") {
          return (
            <label className="label" htmlFor={name}>
              How much do you want to spend?
            </label>
          );
        } else {
          return null;
        }
      };
      render() {
        console.log(`${this.props.name} input renderd`)
        return (
          <div>
            {this.chooseLabel(this.props)}
            <input
              className="Input"
              value={this.props.newVal}
              name={this.props.name}
              placeholder={this.props.name}
              onChange={this.handleChange}
            />
          </div>
        );
      }
    }
    
    class App extends React.Component {
      constructor(props) {
        super(props);
    
        this.state = {
          salary: "",
          cost: "",
          tip: ""
        };
      }
    
      handleInputChange = (e) => {
        this.setState({ [e.target.name]: e.target.value });
      };
      render() {
        console.log("App component and all its children rendered :")
        
        return (
          <div className="App">
            <Input
              name="salary"
              newVal={this.state.salary}
              onInputChange={this.handleInputChange}
            />
            <Input
              name="cost"
              newVal={this.state.cost}
              onInputChange={this.handleInputChange}
            />
            <Input
              name="tip"
              newVal={this.state.tip}
              onInputChange={this.handleInputChange}
            />
          </div>
        );
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      rootElement
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
    <div id="root"></div>

    Was that your question ?