Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Objects and arrays in state

State can hold objects and arrays — for example, the whole task list. The main rule: never change them in place. React compares the old and new value, and if it is the very same array, it decides nothing changed. So you create a new copy with the changes every time.

A task array in state

Move the tasks into state in src/App.jsx:

// src/App.jsx
import { useState } from 'react';
import TodoList from './components/TodoList.jsx';
 
const initialTodos = [
  { id: 1, text: 'Buy milk', done: false },
  { id: 2, text: 'Call Oleg', done: true },
];
 
export default function App() {
  const [todos, setTodos] = useState(initialTodos);
 
  return <TodoList todos={todos} />;
}

The page looks as before, but now the list can change.

Adding

A new array is built with the spread operator ...: all old items plus the new one.

// src/App.jsx
function handleAdd() {
  const todo = { id: crypto.randomUUID(), text: 'New task', done: false };
  setTodos([...todos, todo]);
}
 
// in the markup:
<button onClick={handleAdd}>Add</button>

Each click adds a "New task" item to the end of the list. crypto.randomUUID() is a built-in browser function that gives a unique string for the id.

Removing

Remove with filter — it returns a new array without the unwanted item:

// src/App.jsx
function handleDelete(id) {
  setTodos(todos.filter((todo) => todo.id !== id));
}

Changing an item

To mark a task as done, map creates a new array where that task is replaced by a new object:

// src/App.jsx
function handleToggle(id) {
  setTodos(
    todos.map((todo) => (todo.id === id ? { ...todo, done: !todo.done } : todo)),
  );
}

{ ...todo, done: !todo.done } copies all the task's fields and replaces only done. The other tasks stay the same objects.

How to call handleDelete and handleToggle from TodoItem is covered in the lesson on lifting state up.

An object in state

Objects work the same way: a copy plus the changed field.

// src/components/Profile.jsx
import { useState } from 'react';
 
export default function Profile() {
  const [user, setUser] = useState({ name: 'Anna', city: 'Riga' });
 
  return (
    <button onClick={() => setUser({ ...user, city: 'Berlin' })}>
      {user.name} from {user.city}
    </button>
  );
}

After a click the button shows "Anna from Berlin".

Cheat sheet

ActionInstead ofWrite
Addpush[...arr, item]
Removesplicearr.filter(...)
Changearr[i] = xarr.map(...)
Sortin-place sort[...arr].sort(...) or toSorted
Object fieldobj.x = 1{ ...obj, x: 1 }

Common mistake

Changing an array in place and passing the same array to the setter does not update the page:

// Wrong: React sees the same array and does not re-render.
todos.push(todo);
setTodos(todos);
 
// Correct:
setTodos([...todos, todo]);

If a list "does not update", check for push, splice, sort or assignment to a field.

Thanks for reading!

Donate to the site — we work hard for you.