Lists: For and Index
To render an array — for example, a task list — Solid uses the built-in For component. It creates markup for every element, and when the array changes it adds, removes or moves only the affected nodes. Unlike React, you do not specify keys.
For
// src/components/TodoList.jsx
import { createSignal, For } from 'solid-js';
export default function TodoList() {
const [todos, setTodos] = createSignal([
{ id: 1, title: 'Buy milk' },
{ id: 2, title: 'Call Oleg' },
]);
const add = () => setTodos([...todos(), { id: Date.now(), title: 'New task' }]);
return (
<div>
<ul>
<For each={todos()}>{(todo) => <li>{todo.title}</li>}</For>
</ul>
<button onClick={add}>Add</button>
</div>
);
}Render <TodoList /> in src/App.jsx. The page shows a list of two tasks, and each click adds another one. The existing li elements are not recreated.
The element's index
The function receives the element's index as the second argument. It can change when items move, so it is a getter function:
// src/components/TodoList.jsx
import { For } from 'solid-js';
export default function TodoList(props) {
return (
<ol>
<For each={props.todos} fallback={<li>No tasks</li>}>
{(todo, index) => (
<li>
{index() + 1}. {todo.title}
</li>
)}
</For>
</ol>
);
}Usage: <TodoList todos={todos()} />. The fallback attribute shows "No tasks" while the array is empty.
Index
Index looks similar, but it ties markup to a position in the array rather than to an element:
// src/components/Scores.jsx
import { createSignal, Index } from 'solid-js';
export default function Scores() {
const [scores, setScores] = createSignal([10, 20, 30]);
return (
<div>
<Index each={scores()}>{(score, i) => <p>Place {i + 1}: {score()}</p>}</Index>
<button onClick={() => setScores([15, 20, 30])}>Update</button>
</div>
);
}After the click only the text of the first paragraph changes. Here it is the other way round: the element is a getter score(), and the index i is a plain number.
Which to choose
For | Index | |
|---|---|---|
| Tied to | The element (object) | The position |
| Element | Value todo | Getter score() |
| Index | Getter index() | Number i |
| Good for | Lists of objects: tasks, posts | Simple values: strings, numbers |
Most of the time you need For.
Common mistake
Rendering a list with map, as in React:
// src/components/TodoList.jsx
export default function TodoList(props) {
// Works, but recreates every li on each change
return <ul>{props.todos.map((todo) => <li>{todo.title}</li>)}</ul>;
}map creates all elements anew on any change of the array: focus in fields inside the list is lost and extra work is done. Use For.