Proposal: `useImperativeGetter`
See original GitHub issueI wanted to read an animated value on the JS thread, because I wanted to persist that value to an AsyncStorage so I could use it later.
I learned that I can use call
(thanks to your comment here) to get the current value, but of course I have to wrap it in <Animated.Code>
or useCode
and I also have to do some extra things if I want the value ‘just once’.
In the end I made this masterpiece of a hook which I am very proud of:
import React, {useReducer, Reducer} from 'react';
import Animated, {useCode, cond, call} from 'react-native-reanimated';
type AddListenerAction<T> = {
type: 'add-listener';
resolveFn: (num: T) => void;
};
type RemoveListenerAction<T> = {
type: 'resolve';
value: T;
};
type State<T> = {
inProgress: number;
resolveFns: ((num: T) => void)[];
};
export const useImperativeGetter = <T,>(
value: Animated.Node<T>
): (() => Promise<T>) => {
const [s, dispatch] = useReducer<
Reducer<State<T>, AddListenerAction<T> | RemoveListenerAction<T>>
>(
(state, action) => {
switch (action.type) {
case 'add-listener':
return {
inProgress: 1,
resolveFns: [...state.resolveFns, action.resolveFn]
};
case 'resolve':
return {
inProgress: 0,
resolveFns: []
};
default:
return state;
}
},
{
inProgress: 0,
resolveFns: []
}
);
const callback = React.useCallback(
v => {
dispatch({
type: 'resolve',
value: v[0]
});
for (const fn of s.resolveFns) {
fn(v[0]);
}
},
[s.resolveFns]
);
useCode(() => cond(s.inProgress, call([value], callback)), [
s.inProgress,
value
]);
return () =>
new Promise<T>(resolve => {
dispatch({
type: 'add-listener',
resolveFn: resolve
});
});
};
This is how it’s used:
const value = new Animated.Value(0)
const getValue = useImperativeGetter(value)
now anywhere in your code you can call the returned function to get the current value.
await getValue()
// => 0
I took special care of avoiding concurrency issues or race conditions. So for example you can call it multiple times Promise.all([getValue(), getValue(), getValue()])
and you should get [0, 0, 0]
. Hence why it uses useReducer
, to batch multiple state updates at once.
Would you like to include this in your library? Is this even a smart thing to do or is there already some easier way to do it in the ecosystem? Would love to hear your opionions.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:4
- Comments:12 (12 by maintainers)
So I don’t need it needs to be included anymore 😃 Anyone can feel free to copy my function. Closing this
With Reanimated 2 coming out we probably need another solution in the long term.
But I still use this function and it worked well, so I would recommend you to just copy the function into your codebase.