make-list-provider
v0.4.1
Published
Use hooks for an array of needful
Downloads
21
Readme
Make List Provider
Do you have a React hook that you need to call some number of times? Would you like to use React hooks to replace your list of classes with react natively? Do you just really like using lots of hooks and need to keep their output in a list?
Make a react list provider to store hook states and actions.
Usage
const [Provider, useItem, useList] = makeListProvider();
Provider
Provider for the list
onChange
: called with a copy of a list when it is changed
return (
<Provider
onChange={setList}
>
<Component name="1" />
{more.map(id => (
<Component key={id} name={id} />
))}
</Provider>
);
useItem
Creates an entry in the Provider's list.
Any value can be supplied as the argument.
Changes to the value will update the Provider's callback and useList
hook.
For ordered providers, useItem
will return the index of the item in the ordered list.
export function Component({ name }) {
const [state, setState] = useState(() => Math.random());
const index = useItem(useMemo(() => ({
name,
state,
setState,
}), [name, state]));
return null;
}
useList
Returns the Provider's list of values.
export function useItemByName(name) {
const list = useList();
const item = list.find(item => item.name === name);
return item;
}
Variants
listProvider
The listProvider
variant is the default behavior.
The list is ordered based on React's useLayoutEffect
call order.
This is very useful for executing functions generated by JSX in order like layering on a Canvas.
const [CanvasProvider, useDraw, useDrawList] = makeListProvider();
function RenderCanvas() {
const drawCallbacks = useDrawList();
// ...
return <canvas ref={ref} />
}
function DrawBox(props) {
// ...
useDraw(drawCallback);
}
function App() {
return (
<CanvasProvider>
<RenderCanvas />
<DrawBox rect={[1,2]} />
<DrawBox rect={[2,3]} />
</CanvasProvider>
);
}
unorderedProvider
The unorderedProvider
variant performs all the above functions without any ordering to the items.
These will appear in any order they are called by React and are uncontrolled.
This is useful for something like tracking many socket connections or local storage states.
const [SocketsProvider, useSocket, useSocketList] = makeUnorderedProvider();
function useSocketByName(name) {
return useSocketList().find(socket => socket.name === name);
}
function Socket(props) {
// ...
useSocket(socket);
}
function App() {
return (
<SocketsProvider>
<Socket port={8080} />
<Socket port={8081} />
</SocketsProvider>
);
}
domProvider
The domProvider
variant orders items based on the DOM subtree.
A ref
must be provided to each useItem
as well as a parentRef
to the Provider
to observe updates.
Using the DOM instead of the render cycle can reduce cpu usage on rarely changed elements.
This is useful for custom select/option components
const [OptionsProvider, useOption, useOptionList] = makeDomProvider();
function Option(props) {
const options = useOptionList();
// ...
const index = useOption(ref, value);
return (
<option ref={ref}>{index}</option>
);
}
function App() {
return (
<select ref={ref}>
<OptionsProvider parentRef={ref}>
<Option />
<Option />
</OptionsProvider>
</select>
);
}
Notes
Ordering Children
Per useLayoutEffect
's call order, a parent element will be ordered after it's children.
To avoid unexpect results, you should not nest children under an element that is calling useItem
.
This behavior may change in the future.
Note: useLayoutEffect
is used only in useListProvider
.
If you need more DOM driven ordering, try useDomProvider
.
function Item({ children }) {
useItem();
return children;
}
function App() {
return (
<Provider>
<Item /> // 1
<Item> // 3
<Item /> // 2
</Item>
<Item /> // 4
</Provider>
);
}
License
Copyright (c) 2021, Michael Szmadzinski. (MIT License)