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
| Question | If 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.