# Selectors

Source: https://signy.dev/documentation/1/reactivity/selectors/

Change the selection, update two rows.

A list that highlights the selected row usually reads the selection in every row. Then each change runs every row's binding. A selector keeps one boolean per key instead, so a change only runs the row that lost the selection and the row that gained it.

```js
const selectedId = new Signal(null);
const isSelected = createSelector(() => selectedId.value);

// In each row
html`<tr class=${() => (isSelected(row.id).value ? 'selected' : '')}>...</tr>`;
```

`isSelected(key)` returns a signal that is true while the source equals the key. Read it with `.value` in a binding. Pass a comparator as the second argument to match another way, such as `(id, user) => user?.id === id`.

## Example

```js
const { Component, Signal, application, html, For, createSelector, createRef } = Signy;

const ROWS = Array.from({ length: 1000 }, (_, i) => ({ id: i + 1, label: `Row ${i + 1}` }));

class Rows extends Component {
  selected = new Signal(null);
  isSelected = createSelector(() => this.selected.value);
  counter = createRef();
  runs = 0;

  // Counts how often a row binding runs
  selectedIn(row) {
    if (this.counter.current) this.counter.current.textContent = ++this.runs;
    return this.isSelected(row.id).value;
  }

  render() {
    return html`
      <p>Row bindings run after the first render: <strong ref=${this.counter}>0</strong></p>
      <div class="overflow-auto" style="max-height: 14rem">
        <table>
          <tbody>
            ${For({
              each: ROWS,
              keyBy: "id",
              children: (row) => html`
                <tr data-selected=${() => String(this.selectedIn(row))}>
                  <td style=${() => (this.isSelected(row.id).value ? "font-weight: 700" : "")}>${row.label}</td>
                  <td><button class="small outlined" onClick=${() => (this.selected.value = row.id)}>Select</button></td>
                </tr>
              `,
            })}
          </tbody>
        </table>
      </div>
    `;
  }
}

application(Rows).mount("#selector-rows");
```

Each Select runs two of the 1,000 row bindings, the row that lost the selection and the row that got it. The first runs one, since no row had it before.
