18

In their tutorial for rendering lists, they say:

The <Index> component is provided for these cases. As a rule of thumb, when working with primitives use <Index>.

and

<For> cares about each piece of data in your array, and the position of that data can change; <Index> cares about each index in your array, and the content at each index can change.

Neither of these sentences makes sense to me. What does "when working with primitives" mean? I am always using an Array. Could someone clarify when to use For vs Index?

artem
  • 46,476
  • 8
  • 74
  • 78
abc
  • 1,141
  • 12
  • 29

2 Answers2

16

What does "when working with primitives" mean? I am always using an Array.

It's about array elements - whether they are primitives, as in an array of strings, or objects.

In short, if you have an array of objects, use <For>. If you have and array of strings, and the array is short or you don't ever insert or remove elements in the middle of the array, use <Index>. Otherwise use <For>. If you are not sure, always use <For>.

The difference is how the DOM is updated when array elements are changed.

<For> always checks if an element was in the array before the change, and moves DOM nodes to reflect the change of element position, without calling the callback to render the element (it will also invoke the index() signal if it was used in the callback to show item position, so the stuff that depends on index() will be updated in place). <For> calls the each callback to render the changed element if the element was not previously in the array.

So, when you insert an element in the middle of the array, the each callback is called only once - to render inserted element, and its result is inserted into DOM in place in the array, as expected.

<Index> does not do that - it's much simpler, it just compares old and new elements at each index, and if they differ, it invokes the item() signal that was passed as parameter to the each callback. The callback itself will not be called, only the stuff inside the callback that depends on the item() signal will be updated in place. <Index> calls each callback only when new elements are added at the end of the array.

It's also explained in the FAQ: for <For>, the each callback received an item value and a signal for item position. For <Index>, it's the opposite - the callback receives a signal for item value, and a number for item position.

You can try this example in the Solid playground - you can open the console to see how many times the callback is called by <For> and <Index>:

import { render } from 'solid-js/web';
import { createSignal, For, Index } from 'solid-js';

function ForCats() {
  const [cats, setCats] = createSignal([
    'Keyboard Cat',
    'Maru',
    'Henri The Existential Cat'
  ]);
  
     setTimeout(() => setCats(['Maru', 'Keyboard Cat', 'Keyboard Cat', 'New Cat']), 2000)

  return (
    <ul>
    <For each={cats()}>{name => {

        console.log(`For: rendered ${name} whole cat`);

      return <li>
        <a target="_blank" href="">
          1: {name}
        </a>
      </li>
    }}</For>
    </ul>
  );
}


function IndexCats() {
  const [cats, setCats] = createSignal([
    'Keyboard Cat',
    'Maru',
    'Henri The Existential Cat'
  ]);
  
     setTimeout(() => setCats(['Maru', 'Keyboard Cat', 'Keyboard Cat', 'New Cat']), 2000)

  return (
    <ul>
    <Index each={cats()}>{name => {

        console.log(`Index: rendered ${name()} whole cat`);

      return <li>
        <a target="_blank" href="">
          1: {name()}
        </a>
      </li>
    }}</Index>
    </ul>
  );
}

render(() => <><ForCats /> <IndexCats/ ></>, document.getElementById('app'))

artem
  • 46,476
  • 8
  • 74
  • 78
  • Thanks for your answer. However, I don't understand why we need to use `` for an array of objects, and use `` for an array of primitives? I don't think there are any differences in terms of comparison between object and primitives – Linden X. Quan Nov 15 '22 at 02:24
  • 1
    The difference is not in comparison, the difference is in rendering. will re-render the DOM for moved or changed array elements. If elements are primitives, the DOM is just a text node showing a primitive value, re-rendering a text node is no big deal. If elements are objects, the DOM is usually more complex, containing whatever is necessary to show the object. will not re-render moved or changed elements, it will move the DOM or perform nested updates, which is more efficient than re-rendering each object as a whole. – artem Nov 15 '22 at 07:01
  • I see. that makes to me. Thanks very much. Big thumbs up – Linden X. Quan Nov 15 '22 at 17:23
  • so i think `Index` create a signal for all elements in the array that is why it will not invoke the callback function if you change the content of one item in array (not add a new one) – Rman__ Feb 20 '23 at 21:54
5

The difference is how items are cached between update cycles.

For uses mapArray internally while Index uses indexArray.

mapArray is useful when working with reference types like an array or an object. For primitives, they don't mean much.

Mapped items are cached using the item itself as the key:

const cache = {};
cache[item] = callback();

Item's position has no effect on how they are cached since their references are used as key. Benefit is an elements position may shift inside the array, but its cache remains intact.

So, For will return the previously rendered list item if you pass the same object. If you pass a different object, even if it has the same values, it will return a new list item. Why, because objects are compared by reference for equality. These two are not equal even though they have the same value: {name: 'John'} === {name: 'John'}.

For indexArray, items are cached using their position in the array. It is better suited for caching primitives because cache remains intact as long as they are in the same position.

Index returns the previously rendered list item if there is cached element for the index value. This manifest itself as fine grained updates. For the compound values (like {name: 'John'}) properties are re-rendered rather than the list items.

If you pass less items between update cycles, excess items will be cleaned up. If you pass more items, new list items will be mounted.

snnsnn
  • 10,486
  • 4
  • 39
  • 44
  • The official document is wrong about `` for array of object and `` for array of primitive values. In fact there is no difference to compare object and primitives. – Linden X. Quan Nov 15 '22 at 02:21