useOptic
ts
function useOptic<T>(optic: Optic<T>): [T, ResultObject];
ts
function useOptic<T>(optic: Optic<T>): [T, ResultObject];
This hook allows your components to subscribe to an optic.
It returns a tuple with the value, and a set of options.
The component re-renders each time the focused value changes.
tsx
import {useOptic ,createState } from "@optics/react";constcountOptic =createState (42);constMyCount = () => {const [count ] =useOptic (countOptic );// set new count valuecountOptic .set (100);return <div >count: {count }</div >;};
tsx
import {useOptic ,createState } from "@optics/react";constcountOptic =createState (42);constMyCount = () => {const [count ] =useOptic (countOptic );// set new count valuecountOptic .set (100);return <div >count: {count }</div >;};
Options
The hook can take a set of options as a second argument.
- denormalize
boolean
default: false
If set to true
the value returned by useOptic
is denormalized: the optics referenced in the value are replaced by their respective focused values.
tsx
constpremiumPlanOptic =createState ({name : "Premium",price : 100,});constuserOptic =createState ({name : "John",plan :premiumPlanOptic ,});const [user ] =useOptic (userOptic , {denormalize : true });
tsx
constpremiumPlanOptic =createState ({name : "Premium",price : 100,});constuserOptic =createState ({name : "John",plan :premiumPlanOptic ,});const [user ] =useOptic (userOptic , {denormalize : true });
Result
The hook returns a result object as second element of the tuple with the following properties:
tsx
const [, {setState ,whenFocused ,whenType ,getOptics ,getOpticsFromMapping }] =useOptic (optic );
tsx
const [, {setState ,whenFocused ,whenType ,getOptics ,getOpticsFromMapping }] =useOptic (optic );
- setState
Dispatch<SetStateAction<T>>
An update function for the optic with a stable reference.
Equivalent to: useCallback((value) => optic.set(value), [optic])
.
- whenFocused
<R>(then: (totalOptic: Optic<T>) => R) => R | null
This function lets you narrow a partial
optic (an optic that might be unfocused)
to a total optic (an optic that's always focused, the default behavior).
The narrowed optic is provided as parameter of the callback, the latter only runs if the narrowing succeeds (if the optic is focused on a value).
tsx
const [, {whenFocused }] =useOptic (userPartialOptic );<>{whenFocused ((userTotalOptic ) => (<UserCard userOptic ={userTotalOptic } />))}</>;
tsx
const [, {whenFocused }] =useOptic (userPartialOptic );<>{whenFocused ((userTotalOptic ) => (<UserCard userOptic ={userTotalOptic } />))}</>;
It can also narrow the type of the focused value from nullable to non-nullable:
tsx
const [, {whenFocused }] =useOptic (nullableStringOptic );whenFocused ((stringOptic ) => {});
tsx
const [, {whenFocused }] =useOptic (nullableStringOptic );whenFocused ((stringOptic ) => {});
The function returns null
if the optic is unfocused.
- whenType
<U extends T>(refine: (value: T) => U | false) => <R>(then: (narrowedOptic: Optic<U>) => R) => R | null
This function lets you narrow the type of the focused value in the optic.
It takes a first callback that narrows the value then returns it,
and a second callback that only runs if the narrowing succeeds and that has the narrowed optic provided as parameter.
tsx
const [, {whenType }] =useOptic (stringOrNumberOptic );<>{whenType ((value ) => typeofvalue === "number" &&value )((numberOptic ) => (<NumericInput numberOptic ={numberOptic } />))}{whenType ((value ) => typeofvalue === "string" &&value )((stringOptic ) => (<Input stringOptic ={stringOptic } />))}</>;
tsx
const [, {whenType }] =useOptic (stringOrNumberOptic );<>{whenType ((value ) => typeofvalue === "number" &&value )((numberOptic ) => (<NumericInput numberOptic ={numberOptic } />))}{whenType ((value ) => typeofvalue === "string" &&value )((stringOptic ) => (<Input stringOptic ={stringOptic } />))}</>;
The function returns null
if the narrowing fails.
It can also take an explicit type guard as first argument:
tsx
functionisFish (pet :Fish |Bird ):pet isFish {return (pet asFish ).swim !==undefined ;}const [, {whenType }] =useOptic (petOptic );whenType (isFish )((fishOptic ) => {});
tsx
functionisFish (pet :Fish |Bird ):pet isFish {return (pet asFish ).swim !==undefined ;}const [, {whenType }] =useOptic (petOptic );whenType (isFish )((fishOptic ) => {});
- getOptics
(getKey: (t: T[number]) => string) => readonly [key: string, optic: Optic<T[number]>][];
Only available if the optic is focused on an array, this function derives a new optic for each element of the array.
An element of the array will always have the same associated optic, even if the array is reordered, appended, prepended, ...
This is thanks to the getKey
function that you provide, which returns a unique key for each element of the array.
tsx
const [, {getOptics }] =useOptic (usersOptic );<>{getOptics ((user ) =>user .id ).map (([key ,userOptic ]) => (<User key ={key }userOptic ={userOptic } />))}</>;
tsx
const [, {getOptics }] =useOptic (usersOptic );<>{getOptics ((user ) =>user .id ).map (([key ,userOptic ]) => (<User key ={key }userOptic ={userOptic } />))}</>;
Since keys serve the same purpose as in React, you can reuse the generated key and pass it to the key
prop of the component.
- getOpticsFromMapping
(getKey: (t: T) => string) => readonly [key: string, optic: Optic<T>][];
This function is similar to getOptics
, but it derive the optics from the values of a mapped
optics instead of an array.
tsx
const [, {getOpticsFromMapping }] =useOptic (userMappedOptic );<>{getOpticsFromMapping ((user ) =>user .id ).map (([key ,userOptic ]) => (<User key ={key }userOptic ={userOptic } />))}</>;
tsx
const [, {getOpticsFromMapping }] =useOptic (userMappedOptic );<>{getOpticsFromMapping ((user ) =>user .id ).map (([key ,userOptic ]) => (<User key ={key }userOptic ={userOptic } />))}</>;