selectorFamily(options)

Returns a function that returns a read-only RecoilValueReadOnly or writeable RecoilState selector.

A selectorFamily is a powerful pattern that is similar to a selector, but allows you to pass parameters to the get and set callbacks of a selector. The selectorFamily() utility returns a function which can be called with user-defined parameters and returns a selector. Each unique parameter value will return the same memoized selector instance.


function selectorFamily<T, Parameter>({
key: string,
get: Parameter => ({get: GetRecoilValue}) => Promise<T> | RecoilValue<T> | T,
dangerouslyAllowMutability?: boolean,
}): RecoilValueReadOnly<T>
function selectorFamily<T, Parameter>({
key: string,
get: Parameter => ({get: GetRecoilValue}) => Promise<T> | RecoilValue<T> | T,
set: Parameter => (
{
get: GetRecoilValue,
set: SetRecoilValue,
reset: ResetRecoilValue,
},
newValue: T | DefaultValue,
) => void,
dangerouslyAllowMutability?: boolean,
}): RecoilState<T>

Where

type ValueOrUpdater<T> = T | DefaultValue | ((prevValue: T) => T | DefaultValue);
type GetRecoilValue = <T>(RecoilValue<T>) => T;
type SetRecoilValue = <T>(RecoilState<T>, ValueOrUpdater<T>) => void;
type ResetRecoilValue = <T>(RecoilState<T>) => void;
  • key - A unique string used to identify the atom internally. This string should be unique with respect to other atoms and selectors in the entire application.
  • get - A function that is passed an object of named callbacks that returns the value of the selector, the same as the selector() interface. This is wrapped by a function which is passed the parameter from calling the selector family function.
  • set? - An optional function that will produce writeable selectors when provided. It should be a function that takes an object of named callbacks, same as the selector() interface. This is again wrapped by another function with gets the parameters from calling the selector family function.

The selectorFamily essentially provides a map from the parameter to a selector. Because the parameters are often generated at the callsites using the family, and we want equivalent parameters to re-use the same underlying selector, it uses value-equality by default instead of reference-equality. (There is an unstable cacheImplementationForParams API to adjust this behavior). This imposes restrictions on the types which can be used for the parameter. Please use a primitive type or an object that can be serialized. Recoil uses a custom serializer that can support objects and arrays, some containers (such as ES6 Sets and Maps), is invariant of object key ordering, supports Symbols, Iterables, and uses toJSON properties for custom serialization (such as provided with libraries like Immutable containers). Using functions or mutable objects, such as Promises, in parameters is problematic.

Example

const myNumberState = atom({
key: 'MyNumber',
default: 2,
});
const myMultipliedState = selectorFamily({
key: 'MyMultipliedNumber',
get: (multiplier) => ({get}) => {
return get(myNumberState) * multiplier;
},
// optional set
set: (multiplier) => ({set}, newValue) => {
set(myNumberState, newValue / multiplier);
},
});
function MyComponent() {
// defaults to 2
const number = useRecoilValue(myNumberState);
// defaults to 200
const multipliedNumber = useRecoilValue(myMultipliedState(100));
return <div>...</div>;
}

Async Query Example

Selector Families are also useful to use for passing parameters to queries. Note that using a selector to abstract queries like this should still be "pure" functions which always return the same result for a given set of inputs and dependency values. See this guide for more examples.

const myDataQuery = selectorFamily({
key: 'MyDataQuery',
get: (queryParameters) => async ({get}) => {
const response = await asyncDataRequest(queryParameters);
if (response.error) {
throw response.error;
}
return response.data;
},
});
function MyComponent() {
const data = useRecoilValue(myDataQuery({userID: 132}));
return <div>...</div>;
}

Destructuring Example

const formState = atom({
key: 'formState',
default: {
field1: "1",
field2: "2",
field3: "3",
},
});
const formFieldState = selectorFamily({
key: 'FormField',
get: field => ({get}) => get(formState)[field],
set: field => ({set}, newValue) =>
set(formState, prevState => {...prevState, [field]: newValue}),
});
const Component1 = () => {
const [value, onChange] = useRecoilState(formFieldState('field1'));
return (
<>
<input value={value} onChange={onChange} />
<Component2 />
</>
);
}
const Component2 = () => {
const [value, onChange] = useRecoilState(formFieldState('field2'));
return (
<input value={value} onChange={onChange} />
);
}