0

Im working on a todo aplication in react using useState, im trying to save user input and then after they click submit push it to the listArray, later to display it... I think im doing something wrong in the updateArray function, but I can seem to understand what.

import React, { useState } from "react";

function App() {
  const listArray = [""];

  const [list, updateList] = useState("");

  function handleChange(event) {
    const { name, value } = event.target;

    updateList(value);
    //console.log(list);
  }
  
  function updateArray() {
    console.log(list);

    listArray.push(list);
    console.log(listArray);
  }

  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <input name="entry" onChange={handleChange} type="text" />
        <button>
          <span onSubmit={updateArray}>Add</span>
        </button>
      </div>
      <div>
        <ul>
          <li>{listArray[0]}</li>
        </ul>
      </div>
    </div>
  );
}

export default App;
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129

4 Answers4

1

Save the current value into the state, and keep the list as well into the state so that it isn't cleared each render cycle.

import React, { useState } from "react";

function App() {
  const [list, updateList] = useState([]);
  const [currentValue, setCurrentValue] = useState()

  function handleChange(event) {
    setCurrentValue(event.target.value)
  }

  function handleClick() {
    updateList([...list, currentValue])
  }

  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <input name="entry" onChange={handleChange} type="text" />
        <button type="button" onClick={handleClick}>
          <span>Add</span>
        </button>
      </div>
      <div>
        <ul>
          {list.map((res) => (
            <li key={res}>{res}</li>
          ))}
        </ul>
      </div>
    </div>
  );
}

export default App;

Also, moving the onClick to the button makes more sense semantically (and even for the UX) but it's up to you.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
LorenzoZGX
  • 36
  • 1
  • I've updated your answer so it focuses on the actual changes you made, which were mostly valid. I reverted the event handlers props since it's unnecessary to wrap those inside additional arrow functions. Please feel free to adapt the text to your own and review the changes I made. – Emile Bergeron Oct 06 '20 at 20:52
1

There are several issues with your current code and I will briefly describe and provide a solution to fix them.

  1. Your functions are working fine and as expected, but in a React application there are few ways to re-render a page or component and changing the local variable is not one of them. So you need to use a local state instead of local listArray variable. Since there is one state already you should either define another state or make your current state an object and put your component related states into it in one place I will go with the second approach, because it's more elegant and cleaner one.
const [state, setState] = useState({ list: [], input: "" });
  1. After you define your state, you need to change them properly without effecting unnecessary ones. You just need to send the previous state, save it in the new one and only change the related state in each function. So with ES6 syntax, updating input state will look like this:
setState((prev) => ({ ...prev, input: value })); // immediate return "({})" of an object with iterating through the previous values "...prev" and updating input "input: value"

NOTE: You can read more about spread operator (...) here.

So your handle and updateArray function will look like this:

function handleChange(event) {
  const { value } = event.target;
  setState((prev) => ({ ...prev, input: value }));
}

function updateArray() {
  setState((prev) => ({ ...prev, list: [...state.list, state.input] }));
}
  1. onSubmit event will only work on forms or submit buttons so you need to change it to onClick. Also, to make the whole button catch the onclick action you need to set it on the button itself, instead of span element.
<button onClick={updateArray}> <!-- It's better to assign onclick action to an element with a function or arrow function to prevent it from running in the first render "(event) => updateArray(event)" -->
  <span>Add</span> 
</button>
  1. And finally you need to map through the updated array of todo items in your JSX.
<ul>
  {state.list.map((item, index) => (
      <li key={index}>{item}</li>
  ))}
</ul>

Working Demo:

CodeSandbox

SMAKSS
  • 9,606
  • 3
  • 19
  • 34
-1

listArray is cleared with every rerender. You should store your data in state. So

const [listArray, setListArray] = useState([])

And updateArray should look like:

function updateArray() {
    setListArray([...listArray, list]);
}

I guess, in updateArray function should be some logic to clear list

DamianM
  • 468
  • 1
  • 6
  • 15
-2

Notice how listArray will always go back to the default value when your app component re-renders (using useState may re-render the component)

I would instead make the string the user inputs a normal const and use useState for the array so the values in the array will be saved across re-renders