简体   繁体   中英

Input tag looses focus on state update in react

This is a very simple app, I have declared 2 states for 2 cases

In case 1, I am using the state dataArray1 and creating an array of input tag.

In case 2, I am using state dataArray2 and creating an array of where just contains a input tag

Now the issue is this, in case1 UI is functioning as required but in case 2 the input loses its focus when we type anything. I want to understand why is this happens and how to fix it...

link for codesandbox:https://codesandbox.io/s/dreamy-dubinsky-x8t6c?file=/src/App.js

Here is the code...

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [dataArray1, setDataArray1] = useState([
    "Hello",
    "My name is testBot",
    "I am a student"
  ]);

  const [dataArray2, setDataArray2] = useState([
    "Hello",
    "My name is testBot",
    "I am a student"
  ]);

  const generateUI1 = (data) => {
    console.log(data);
    return data.map((e, i) => {
      return (
        <div key={`type1Key${i}`}>
          <input
            value={data[i]}
            onChange={(e) => {
              let newData = [...data];
              newData[i] = e.target.value;
              setDataArray1(newData);
            }}
          />
        </div>
      );
    });
  };

  const InputComponent = (props) => {
    return (
      <input
        value={props.data[props.i]}
        onChange={(e) => {
          let newData = [...props.data];
          newData[props.i] = e.target.value;
          setDataArray2(newData);
        }}
        // key={`new${props.i}`}
      />
    );
  };
  const generateUI2 = (data) => {
    console.log(data);
    return data.map((e, i) => {
      return (
        <div key={`${data[i]}`}>
          <InputComponent data={data} i={i} key={`type2Key${i}`} />
        </div>
      );
    });
  };

  return (
    <div className="App">
      <div>
        <h1>Case 1</h1>
        {generateUI1(dataArray1)}
      </div>
      <div>
        <h1>Case 2</h1>
        {generateUI2(dataArray2)}
      </div>
    </div>
  );
}

You are causing re-renders which will result in losing focus. So you can either move your component outside of the scope App() as the user "zb22" described in his answer. Or alternatively of calling your component like this:

<InputComponent data={data} i={i} />

You can call it like a method:

InputComponent({ data: data, i: i })

Also, keep in mind to not change your keys together with your data. Right now your key changes when your data changes, this will cause a re-render So change your key from:

<div key={`${data[i]}`}>

to something like:

<div key={`ui2_${i}`}>

sandbox

it's like component inside a component,

you need to separate InputComponent component from the App component. and pass setDataArray2 to it as a prop.

import { useState } from "react";
import "./styles.css";

const InputComponent = (props) => {
  return (
    <input
      value={props.data[props.i]}
      onChange={(e) => {
        let newData = [...props.data];
        newData[props.i] = e.target.value;
        props.setDataArray2(newData);
      }}
    />
  );
};

export default function App() {
  const [dataArray1, setDataArray1] = useState([
    "Hello",
    "My name is testBot",
    "I am final year student"
  ]);

  const [dataArray2, setDataArray2] = useState([
    "Hello",
    "My name is testBot",
    "I am final year student"
  ]);

  const generateUI1 = (data) => {
    console.log(data);
    return data.map((e, i) => {
      return (
        <div key={`type1Key${i}`}>
          <input
            value={data[i]}
            onChange={(e) => {
              let newData = [...data];
              newData[i] = e.target.value;
              setDataArray1(newData);
            }}
          />
        </div>
      );
    });
  };

  const generateUI2 = (data) => {
    console.log(data);
    return data.map((e, i) => {
      return (
        <div key={`type1Key${i}`}>
          <InputComponent data={data} i={i} setDataArray2={setDataArray2}/>
        </div>
      );
    });
  };

  return (
    <div className="App">
      <h3>This is a very simple app, I have declared 2 states for 2 cases</h3>
      <ol>
        <li>
          {` In case 1, I am using the state dataArray1 and creating a array of input tag
`}
        </li>
        <li>
          {`In case 2, I am using state dataArray2 and creating an array of
          <InputComponent /> where <InputComponent /> just contains a input tag`}
        </li>
      </ol>
      <p>
        Now the issue is this, in case1 UI is functioning as required but in
        case 2 the input loses its focus when we type anything.
      </p>

      <div>
        <h1>Case 1</h1>
        {generateUI1(dataArray1)}
      </div>
      <div>
        <h1>Case 2</h1>
        {generateUI2(dataArray2)}
      </div>
    </div>
  );
}

check it in this sandbox

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