Search code examples
javascriptreactjsformscomponents

React.js onChange handler changes all input field when typing.. How do I get each one to type when it is the event target?


I am working on an old app and I am trying to restructure the form that will be used to perform crud on my backend. I decided to make the input reusable, however, I have an issue that when I type into one box I type into all of them, that is, they are all the event target at once! how do I make it so only the input in focus will be typed into?

//update.js

import React, { Component } from "react";
import axios from "../../node_modules/axios";
import Input from "./input";
import "./update.css";

class Update extends Component {
  constructor(props) {
    super(props);
    this.state = {
      _id: "",
      brand: "",
      name: "",
      price: "",
      imageLink: "",
      productLink: "",
      category: "",
      productType: "",
    };
    this.handleChange = this.handleChange.bind();
    this.onSubmit = this.onSubmit.bind();
  }
  handleChange = (evt) => {
    this.setState({ [evt.target.name]: evt.target.value });
  };

  onSubmit = (evt) => {
    evt.preventDefault();
    console.log(this.state);
    axios
      .put(
        `https://makeupinfo.herokuapp.com/product${this.state._id}`,
        this.state
      )
      .then((res) => {
        console.log(res);
      })
      .then((err) => {
        console.log(err);
      });
  };

  render() {
    const {
      _id,
      brand,
      name,
      price,
      imageLink,
      productLink,
      productCategory,
      productType,
    } = this.state;
    const info = [
      { name: "_id", placeholder: "product ID", value: _id },
      { name: "brand", placeholder: "brand", value: brand },
      { name: "name", placeholder: "product name", value: name },
      { name: "price", placeholder: "price", value: price },
      { name: "image-link", placeholder: "image link", value: imageLink },
      { name: "product-link", placeholder: "product link", value: productLink },
      {
        name: "product category",
        placeholder: "product category",
        value: productCategory,
      },
      { name: "product type", placeholder: "product type", value: productType },
    ];

    return (
      <div>
        <h2 className='links'>Search by ID and update</h2>
        <div className='form-container'>
          <Input props={info}></Input>
        </div>
      </div>
    );
  }
}
export default Update;

and the child component:

import React from "react";

class Input extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: "",
    };
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange = (e) => {
    e.preventDefault();
    this.setState({ value: e.target.value });
    console.log(this.state.value);
  };
  render() {
    const data = this.props.props;
    const dataArray = data.map((item) => (
      <input
        className='form-control form-control-sm'
        name={item.name}
        type='text'
        placeholder={item.placeholder}
        value={this.state.value}
        onChange={this.handleChange}
        key={item.name}
      />
    ));
    return <div>{dataArray}</div>;
  }
}

export default Input;

Solution

  • All your inputs share the same state.value from the Input component. So when you edit one, you edit all of them. Your Input component should only render one input, and you should move info.map to the Update component.

    Right now you call map inside Input which generate all the inputs with one shared value. If you do the map in your Update component, you will render one input for each Input, and they will all have their own value, so no more side effect.