Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Derived values

Not everything that changes on screen has to live in state. If a value can be computed from props or other state, compute it right during rendering. Such a value is called derived. The less state you have, the fewer places where data can get out of sync.

A task counter

How many tasks are left is not separate state but the result of counting the todos array:

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

The page shows "Tasks left: 1". Whenever todos changes, React calls App again and left is recomputed by itself.

A list filter

State holds only the user's choice — which filter is selected. The filtered list itself is computed:

// src/App.jsx
const [filter, setFilter] = useState('all');
 
const visible = todos.filter((todo) => {
  if (filter === 'active') return !todo.done;
  if (filter === 'done') return todo.done;
  return true;
});
 
// in the markup:
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('done')}>Done</button>
<TodoList todos={visible} />

The buttons switch the filter and the list below changes. Adding or deleting a task is also reflected in the filtered list right away.

How to decide whether you need state

QuestionIf yes
Does the value come from props?Not state, use the prop
Can it be computed from other state?Not state, compute it
Does it never change over time?Not state, a plain constant
None of the above?It is state

For the to-do list only todos and filter are state. The number of tasks left, the "No tasks" text and the visible list are derived.

Expensive calculations

Counting over an array of a hundred tasks takes a fraction of a millisecond, so speed is not a concern. If a calculation is truly heavy, it can be remembered with the useMemo hook — it has its own lesson.

Common mistake

A copy of a derived value in separate state quickly goes stale:

// Wrong: left has to be updated by hand on every change of todos.
const [todos, setTodos] = useState(initialTodos);
const [left, setLeft] = useState(1);
 
// Correct:
const left = todos.filter((todo) => !todo.done).length;

Add a task, forget to call setLeft — and the page shows the wrong number. A derived value always matches the data.

Thanks for reading!

Donate to the site — we work hard for you.