Home > Back-end >  How to keep previous values when updating array in React with useState
How to keep previous values when updating array in React with useState

Time:10-06

I got a project where I am splitting the words from different inputfields and push these to a content array. The array should 'remember' all the words that have been pushed in it. Currently, whenever the user starts typing in a 'new' (and empty) inputfield, the content array is cleared because the value is empty.

I got a codesandbox setup here.

So lets say the user types lorem ipsum dolar in the first inputfield and sit amet in the second inputfield, the content array should like like this: ['lorem', 'ipsum', 'dolar', 'sit', 'amet']

How can I achieve this result?

Code for reference

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

export default function App() {
  const [fields, setFields] = useState([
    { id: 1, value: "" },
    { id: 2, value: "" }
  ]);

  const [content, setContent] = useState([]);

  const handleOnChange = (e, idx) => {
    let newField = [...fields];
    newField[idx].value = e.target.value;
    setFields(newField);

    //How do I push both the values from field 1 AND 2 in the array?
    //Currently, when you switch from inputfield, the content array gets reset
    let words = e.target.value.split(" ");
    setContent(words);
  };

  return (
    <div>
      {fields.map((field, idx) => (
        <div>
          <span>Field {idx}</span>
          <input
            className="border-2 border-red-500"
            type="text"
            key={field.id}
            value={field.value}
            onChange={(e) => handleOnChange(e, idx)}
          />
        </div>
      ))}
      <div style={{ marginTop: "20px", backgroundColor: "gray" }}>
        {content.map((item, idx) => (
          <div key={idx}>{item}</div>
        ))}
      </div>
    </div>
  );
}

CodePudding user response:

The content is derived from the fields state value properties, and you don't actually need to store it in a state. Compute the content on each render.

const { useState } = React;

function App() {
  const [fields, setFields] = useState([
    { id: 1, value: "" },
    { id: 2, value: "" }
  ]);

  const handleOnChange = (e, idx) => {
    setFields(prevFields => {
      const newFields = [...fields];    
      newFields[idx] = {
        ...newFields[idx],
        value: e.target.value
      };
      
      return newFields;
    });
  };
  
  const content = fields.map(o => o.value).join(' ').split(' ');

  return (
    <div>
      {fields.map((field, idx) => (
        <div key={field.id}>
          <span>Field {idx}</span>
          <input
            className="border-2 border-red-500"
            type="text"
            value={field.value}
            onChange={(e) => handleOnChange(e, idx)}
          />
        </div>
      ))}
      <div style={{ marginTop: "20px", backgroundColor: "gray" }}>
        {content.map((item, idx) => (
          <div key={idx}>{item}</div>
        ))}
      </div>
    </div>
  );
}

ReactDOM.render(
  <App />,
  root
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

CodePudding user response:

This syntax allow to add the element to current existing array:

setMyArray(oldArray => [...oldArray, newElement]);

CodePudding user response:

Two things needs to be changed:

  1. use onBlur to push your words in content.
  2. First push previous words then new words.

working example: https://codesandbox.io/s/goofy-colden-8h5v5

    import "./styles.css";
    import { useState } from "react";
    
    export default function App() {
      const [fields, setFields] = useState([
        { id: 1, value: "" },
        { id: 2, value: "" }
      ]);
    
      const [content, setContent] = useState([]);
    
      const handleOnChange = (e, idx) => {
        let newField = [...fields];
        newField[idx].value = e.target.value;
        setFields(newField);
      };
    
      const onBlur = (e, idx)=> {
            //How do I push both the values from field 1 AND 2 in the array?
        //Currently, when you switch from inputfield, the content array gets reset
        let words = fields[idx].value.split(" ");
        setContent([...content, ...words]);
      }
    
      return (
        <div>
          {fields.map((field, idx) => (
            <div>
              <span>Field {idx}</span>
              <input
                className="border-2 border-red-500"
                type="text"
                key={field.id}
                value={field.value}
                onChange={(e) => handleOnChange(e, idx)}
                onBlur={(e) => onBlur(e, idx)}
              />
            </div>
          ))}
          <div style={{ marginTop: "20px", backgroundColor: "gray" }}>
            {content.map((item, idx) => (
              <div key={idx}>{item}</div>
            ))}
          </div>
        </div>
      );
    }
  • Related