Skip to main content

useSelectorCreator

useSelectorCreator is a Reflex Hook that lets you memoize and subscribe to a selector factory's state from your component.

const state = useSelectorCreator(factory, ...args);

Reference​

useSelectorCreator(factory, ...args)​

useSelectorCreator creates a selector with the given selector factory and args, and uses the result to subscribe to the state. It returns the selected state.

import { useSelectorCreator } from "@rbxts/roact-reflex";
import { selectTodo } from "./selectors";

function Todo({ id }: Props) {
const todo = useSelectorCreator(selectTodo, id);
// ...
}

The selector returned by the factory will be memoized in useMemo with the given args to preserve the cache. If the args change, the selector will be re-created.

See more examples below.

Parameters​

  • factory - A function that returns a selector for the given arguments.
  • ...args - Arguments to pass to the selector factory and memoize with.

Returns​

useSelectorCreator returns the selected value from the root producer's state. If the value changes, the component will re-render.

caveats
  • Avoid creating new objects and passing them to ...args, as this will cause useSelectorCreator to re-create the selector every re-render. If you need to pass an object, memoize it with useMemo.

Usage​

Subscribing to state with selector factories​

Selector factories are useful for creating selectors that depend on external arguments. For example, a selector that selects a todo by its ID might look like this:

const selectTodo = (id: number) => {
return createSelector(selectTodos, (todos) => {
return todos.find((todo) => todo.id === id);
});
};

Roact Reflex provides useSelector to connect a function component to selectors, but it's not safe to create a selector inside of a component without memoizing it. This is because the selector will be re-created on every render, which will create a new cache, causing further re-renders.

To solve this, you might try to memoize the selector with the useMemo hook:

import { selectTodo } from "./selectors";

function Todo({ id }: Props) {
const selector = useMemo(() => {
return selectTodo(id);
}, [id]);

const todo = useSelector(selector);

// ...
}

This works, and it's essentially what useSelectorCreator does. It creates a selector with the given arguments, memoizes it with the arguments you passed, and subscribes to the selector's state.

import { useSelectorCreator } from "@rbxts/roact-reflex";
import { selectTodo } from "./selectors";

function Todo({ id }: Props) {
const todo = useSelectorCreator(selectTodo, id);
// ...
}

Troubleshooting​

useSelectorCreator is creating a new selector every render​

If your selector factory is being called on every render, make sure you're not creating new arrays or objects and passing them to ...args. This will cause useSelectorCreator to re-create the selector every render.

Here's an example of what not to do:

import { selectTodos } from "./selectors";

function Todo() {
// 🔴 This array is not memoized
const todos = useSelectorCreator(selectTodos, [1, 2, 3]);
// ...
}

Instead, you should memoize the array with useMemo:

import { selectTodos } from "./selectors";

function Todo() {
// ✅ This array is memoized
const ids = useMemo(() => [1, 2, 3], []);
const todos = useSelectorCreator(selectTodos, ids);
// ...
}