Skip to main content

selectorFamily

Similarly to atomFamily, the use case for selectorFamily is mapping values to Stan primitives. It returns a memoized function that produces selectors based on a given parameter (which must be serializable).

Among other things, it can be especially useful for:

  • Building selectors with context-dependent behavior
  • Memoizing selectors that perform expensive computations, make API calls, and so on
const selectorFamily: <T, P extends SerializableParam>(
selectorFamilyFn: SelectorFamilyFn<T, P>,
options?: SelectorFamilyOptions<P>,
) => (param: P) => Scoped<ReadonlyState<T>>;
  • selectorFamilyFn - A function with the following signature: <T, P extends SerializableParam>(param: P) => SelectorFn<T>. It takes one serializable parameter and returns a selector function (see selector for details).
  • options? - Selector family configuration:
    • tag? - A string identifier (see selector for details). Alternatively, it can be a function with the following signature: <P extends SerializableParam>(param: P) => string. The function form is useful when the tag should depend on the parameter.
    • areValuesEqual? - A function used to determine whether two consecutive selector values are equal (see selector for details).
    • cachePolicy? - Configures the behavior of the selector family cache. Possible values:
      • { type: 'keep-all' } (default) - A selector instance will be cached for every parameter.
      • { type: 'most-recent' } - Only the last parameter's selector instance will be cached.
      • { type: 'lru'; maxSize: number } - Only the most recent maxSize selector instances will be cached.
info

Stan does not rely on referential equality for selectorFamily parameters, so there's no need to maintain stable references. Cache keys are computed by serializing (stable stringification) the parameter values - hence the serializability requirement.

Example

Get user by id:

const userById = selectorFamily<Promise<User>, string>(
userId => () => getUser(userId),
);

Same, but abort the pending request:

const userById = selectorFamily<Promise<User>, string>(
userId =>
({ signal }) =>
getUser(userId, { signal }),
);

Same, but let's cache only up to 5 requests:

const userById = selectorFamily<Promise<User>, string>(
userId =>
({ signal }) =>
getUser(userId, { signal }),
{
cachePolicy: {
type: 'lru',
maxSize: 5,
},
},
);

Map the result to a different value:

const userNameById = selectorFamily<Promise<string>, string>(
userId =>
async ({ get }) => {
const { name } = await get(userById(userId));

return name;
},
);

Render with React:

const MyComponent: FC<{ userId: string }> = ({ userId }) => {
const result = useStanValueAsync(userNameById(userId));

switch (result.type) {
case 'loading':
return <p>Loading&hellip;</p>;

case 'error':
return <p>Nope</p>;

case 'ready':
return <p>Name: {result.value}</p>;
}
};

See also