简体   繁体   中英

React doesn't change input value with looping array for initial value

How can i update a value for input text in a state array? Here is the code that I am currently using:

This is for initial state react hooks

const [kernelSize, setKernelSize] = useState(3)
const [kernel, setKernel] = useState([])

Input range for size of kernel

<input type="range" step="2" name="range" min="3" max="7" value={kernelSize} className="slider" onChange={(e) => handleKernel(e)} />

This is for handleKernel function

const handleKernel = (e) => {
    setKernelSize(e.target.value)
    let kernel = []
    for (let i = 0; i < e.target.value; i++) {
        kernel.push([])
        for (let j = 0; j < e.target.value; j++) {
            kernel[i].push(1)
        }
    }
    setKernel(kernel)
}

This is for looping input text based size/length from input range

{
kernel.map((value, index) => {
    return (
        <div className="row mt-3" key={`${index}_${value}`}>
            {
                value.map((value_2, index_2) => {
                    return (
                        <div className="input-wrap" key={`${index_2}_${value_2}`}>
                            <input type="text" id="" className="form-control" value={value_2} onChange={(e) => updateKernel(e, index, index_2)} />
                        </div>
                    )
                })
            }
        </div>
    )
})}

This is for updateKernel function

const updateKernel = (e, index, index_2) => () => {
    let kernelCopy = [...kernel];
    kernelCopy[index][index_2] = e.target.value;
    setKernel(kernelCopy);
}

Looks like the issue here is that you aren't also shallow copying the nested inner array that is being updated.

const updateKernel = (e, index, index_2) => {
  // use a functional state update to update from previous state
  setKernel((kernel) =>
    // shallow copy outer array
    kernel.map((outerEl, i1) =>
      i1 === index
        // shallow copy inner array on index match
        ? outerEl.map((innerEl, i2) =>
            // update at index match or return current
            i2 === index_2 ? e.target.value : innerEl 
          )
        : outerEl
    )
  );
};

Update

Inputs lose focus because of the way you've defined the React keys. The keys use the index and current value, both of which are probably the two worst values you could choose for React keys. The issue is that upon each change the key is now a different value and React unmounts the previous input and mounts a new one.

The solution is to use stable keys that persist and live for the entire life of the data it's representing in React. For this I suggest using objects with an id property for the key, and a value property for the value being rendered.

Here's a full code example using the uuid package to generate GUIds.

import { v4 as uuidV4 } from "uuid";

function App() {
  const [kernelSize, setKernelSize] = useState(3);
  const [kernel, setKernel] = useState([]);

  useEffect(() => {
    let kernel = [];
    for (let i = 0; i < kernelSize; i++) {
      kernel.push({
        id: uuidV4(), // <-- generate a row id
        value: []
      });
      for (let j = 0; j < kernelSize; j++) {
        kernel[i].value.push({
          id: uuidV4(), // <-- generate element id
          value: 1
        });
      }
    }
    setKernel(kernel);
  }, [kernelSize]);

  const handleKernel = (e) => {
    setKernelSize(e.target.value);
  };

  const updateKernel = (index, index2) => (e) => {
    setKernel((kernel) =>
      kernel.map((outerEl, i1) =>
        i1 === index
          ? {
              ...outerEl,
              value: outerEl.value.map((innerEl, i2) =>
                i2 === index2
                  ? {
                      ...innerEl,
                      value: e.target.value
                    }
                  : innerEl
              )
            }
          : outerEl
      )
    );
  };

  return (
    <div className="App">
      <input
        type="range"
        step="2"
        name="range"
        min="3"
        max="7"
        value={kernelSize}
        className="slider"
        onChange={handleKernel}
      />

      <div>
        {kernel.map((row, index) => {
          return (
            <div className="row mt-3" key={row.id}> // <-- use id as React key
              {row.value.map(({ id, value }, index_2) => {
                return (
                  <span className="input-wrap" key={id}> // <-- use id as React key
                    <input
                      style={{
                        width: "2rem"
                      }}
                      type="text"
                      id=""
                      className="form-control"
                      value={value}
                      onChange={updateKernel(index, index_2)}
                    />
                  </span>
                );
              })}
            </div>
          );
        })}
      </div>
    </div>
  );
}

编辑 react-doesnt-change-input-value-with-looping-array-for-initial-value

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