import { useList } from "effector-react";
since

useList introduced in effector-react 20.1.1

Hook function for efficient rendering of list store. Every item will be memoized and updated only when their data change.

When should you use useList?

useList is designed to solve the specific task of efficiently rendering lists. With useList, you don’t need to manually set key for list components, and it implements a more optimized re-rendering process. If you feel that something else is needed, it means the feature has outgrown useList, and you should use useStoreMap. With useStoreMap, you can extract specific data from the store in an optimal way, especially if you don’t need the entire store, but only a part of it

API

useList($store, fn)

Using index as key for each element in the list.

Formulae

useList(
  $store: Store<T[]>,
  fn: (value: T, index: number) => React.ReactNode,
): React.ReactNode;

Arguments

  1. $store (Store<T>): Store with an array of items
  2. fn (Function): Render function which will be called for every item in list

Returns

(React.Node)

Examples

Basic

import { createStore } from "effector";
import { useList } from "effector-react";

const $users = createStore([
  { id: 1, name: "Yung" },
  { id: 2, name: "Lean" },
  { id: 3, name: "Kyoto" },
  { id: 4, name: "Sesh" },
]);

const App = () => {
  // we don't need keys here any more
  const list = useList($users, ({ name }, index) => (
    <li>
      [{index}] {name}
    </li>
  ));

  return <ul>{list}</ul>;
};

Try it

With store updates

import { createStore, createEvent } from "effector";
import { useList, useUnit } from "effector-react";

const todoSubmitted = createEvent();
const todoToggled = createEvent();

const $todoList = createStore([
  { text: "write useList example", done: true },
  { text: "update readme", done: false },
]);

$todoList.on(todoToggled, (list, id) =>
  list.map((todo, index) => {
    if (index === id)
      return {
        ...todo,
        done: !todo.done,
      };
    return todo;
  }),
);

$todoList.on(todoSubmitted, (list, text) => [...list, { text, done: false }]);

todoSubmitted.watch((e) => {
  e.preventDefault();
});

const TodoList = () => {
  const [onTodoToggle] = useUnit([todoToggled]);
  return useList($todoList, ({ text, done }, index) => {
    const todo = done ? (
      <del>
        <span>{text}</span>
      </del>
    ) : (
      <span>{text}</span>
    );

    return <li onClick={() => onTodoToggle(index)}>{todo}</li>;
  });
};

const App = () => {
  const [onTodoSubmit] = useUnit([todoSubmitted]);

  function handleSubmit(e) {
    e.preventDefault();
    onTodoSubmit(e.currentTarget.elements.content.value);
  }

  return (
    <div>
      <h1>todo list</h1>
      <form onSubmit={handleSubmit}>
        <label htmlFor="content">New todo</label>
        <input type="text" name="content" required />
        <input type="submit" value="Add" />
      </form>
      <ul>
        <TodoList />
      </ul>
    </div>
  );
};

Try it

useList($store, config)

Used when you need to pass dependencies to react (to update items when some of its dependencies are changed).

By default, useList rerenders only when some of its items were changed. However, sometimes we need to update items when some external value (e.g. props field or state of another store) changes. In such case, we need to tell React about our dependencies and pass keys explicitly.

Formulae

useList(
  $store: Store<T[]>,
  config: {
    keys: any[],
    getKey?: (value: T) => React.Key,
    fn: (value: T, index: number) => React.ReactNode,
    placeholder?: React.ReactNode,
  }
): React.ReactNode;

Arguments

  1. $store (Store<T>): Store with an array of items
  2. config (Object)
    • keys (Array): Array of dependencies, which will be passed to react by useList
    • fn ((value: T) => React.ReactNode): Render function which will be called for every item in list
    • getKey ((value) => React.Key): Optional function to compute key for every item of list
    • placeholder (React.ReactNode): Optional react node to render instead of an empty list
since

getKey option introduced in effector-react@21.3.0

since

placeholder option introduced in effector-react@22.1.0

Returns

(React.Node)

Examples

Basic

import ReactDOM from "react-dom";
import { createEvent, createStore, restore } from "effector";
import { useUnit, useList } from "effector-react";

const renameUser = createEvent();

const $user = createStore("alice");
const $friends = createStore(["bob"]);

$user.on(renameUser, (_, name) => name);

const App = () => {
  const user = useUnit($user);

  return useList($friends, {
    keys: [user],
    fn: (friend) => (
      <div>
        {friend} is a friend of {user}
      </div>
    ),
  });
};

ReactDOM.render(<App />, document.getElementById("root"));
// => <div> bob is a friend of alice </div>

setTimeout(() => {
  renameUser("carol");
  // => <div> bob is a friend of carol </div>
}, 500);

Try it

Contributors