There are For
and Index
Components in SolidJS
to render array elements efficiently. array.map
is inefficient as it always maps the entire array.
For
The For component is the best way to loop over an array of objects. As the array changes, updates or moves items in the DOM rather than recreating them.
Index
Solid also provides the Index component, which will cause less rerenders in certain situations.
For Example
import { For, createSignal } from "solid-js";
const initialPeople = [
{
name: "Amy",
age: 15,
},
{
name: "Bob",
age: 25,
},
{
name: "Charlee",
age: 20,
},
];
function ForComp() {
const [people, setPeople] = createSignal(initialPeople);
const changeAge = (personIdx) => () => {
const newPeople = [...people()];
newPeople[personIdx] = {
...newPeople[personIdx],
age: Math.floor(Math.random() * 30),
};
setPeople(newPeople);
};
return (
<>
<For each={people()}>
{(person, personIdx) => {
console.log(`person(${personIdx()}) has rendered.`);
return (
<div style={{ cursor: "pointer" }} onClick={changeAge(personIdx())}>
<h6>
Name: {person.name} Age: {person.age}
</h6>
</div>
);
}}
</For>
</>
);
}
function App() {
return <ForComp />;
If the second item is clicked, the age of the second item will be changed like the following image.
If you change a part of an object, there would be no change on display.
const changeAge = (personIdx) => () => {
const newPeople = [...people()];
newPeople[personIdx].age = Math.floor(Math.random() * 30);
setPeople(newPeople);
};
This doesn't work because even though a field of the object is changed, the object remains the same.
Index Example
function IndexComp() {
const [people, setPeople] = createSignal(initialPeople);
const changeAge = (personIdx) => () => {
const newPeople = [...people()];
newPeople[personIdx] = {
...newPeople[personIdx],
age: Math.floor(Math.random() * 30),
};
setPeople(newPeople);
};
return (
<>
<Index each={people()}>
{(person, personIdx) => {
console.log(`INDEX: person(${personIdx}) has rendered.`);
return (
<div style={{ cursor: "pointer" }} onClick={changeAge(personIdx)}>
<h6>
Name: {person().name} Age: {person().age}
</h6>
</div>
);
}}
</Index>
</>
);
}
function App() {
return (
<>
<h1>For</h1>
<ForComp />
<hr />
<h1>Index</h1>
<IndexComp />
</>
);
}
Index
Component re-renders when index is changed while For
component re-renders when its value is changed.
But you can still see the data is changed because person()
is used in return
.
return (
<>
<Index each={people()}>
{(person, personIdx) => {
console.log(`INDEX: person(${personIdx}) has rendered.`);
const p = person();
return (
<div style={{ cursor: "pointer" }} onClick={changeAge(personIdx)}>
<h6>
Name: {p.name} Age: {p.age}
</h6>
</div>
);
}}
</Index>
</>
);
If you person() is used inside the render function, the data won't be changed. It's how SolidJS
works.
Add a new item
import { For, createSignal } from "solid-js";
const initialPeople = [
{
name: "Amy",
age: 15,
},
{
name: "Bob",
age: 25,
},
{
name: "Charlee",
age: 20,
},
];
function ForComp() {
const [people, setPeople] = createSignal(initialPeople);
const changeAge = (personIdx) => () => {
const newPeople = [...people()];
newPeople[personIdx] = {
...newPeople[personIdx],
age: Math.floor(Math.random() * 30),
};
setPeople(newPeople);
};
const addNewPerson = () => {
setPeople(
people().concat({
people: "Delta",
age: 50,
})
);
};
return (
<>
<button onClick={addNewPerson}>Add New Person</button>
<For each={people()}>
{(person, personIdx) => {
console.log(`FOR: person(${personIdx()}) has rendered.`);
return (
<div style={{ cursor: "pointer" }} onClick={changeAge(personIdx())}>
<h6>
Name: {person.name} Age: {person.age}
</h6>
</div>
);
}}
</For>
</>
);
}
function IndexComp() {
const [people, setPeople] = createSignal(initialPeople);
const changeAge = (personIdx) => () => {
const newPeople = [...people()];
newPeople[personIdx] = {
...newPeople[personIdx],
age: Math.floor(Math.random() * 30),
};
setPeople(newPeople);
};
const addNewPerson = () => {
setPeople(
people().concat({
people: "Delta",
age: 50,
})
);
};
return (
<>
<button onClick={addNewPerson}>Add New Person</button>
<Index each={people()}>
{(person, personIdx) => {
console.log(`INDEX: person(${personIdx}) has rendered.`);
const p = person();
return (
<div style={{ cursor: "pointer" }} onClick={changeAge(personIdx)}>
<h6>
Name: {p.name} Age: {p.age}
</h6>
</div>
);
}}
</Index>
</>
);
}
function App() {
return (
<>
<h1>For</h1>
<ForComp />
<hr />
<h1>Index</h1>
<IndexComp />
</>
);
}
For the new item, both renders the new item as well.
Conclusion
If you expect the data change doesn't need to be affected to rendering and index
only matters, Index
would be a good option then For
for better performance.
I hope you found it useful in this article.
Happy Coding!
Top comments (0)