Created
August 31, 2020 23:27
-
-
Save likern/9ea94b38fe51cfab358afa239281e989 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useCallback, useEffect } from 'react'; | |
import { LayoutChangeEvent, Text } from 'react-native'; | |
import Animated, { | |
useSharedValue, | |
useAnimatedRef, | |
runOnUI, | |
measure | |
} from 'react-native-reanimated'; | |
function useAnimatedState<T>(initial: T) { | |
const [state, setState] = useState(initial); | |
const sv = useSharedValue(initial); | |
function setMixedState(newValue: (arg: T) => T): void; | |
function setMixedState(newValue: T): void; | |
function setMixedState(newValue: any) { | |
'worklet'; | |
if (typeof newValue === 'function') { | |
sv.value = newValue(sv.value); | |
setState(newValue); | |
} else { | |
sv.value = newValue; | |
setState(newValue); | |
} | |
} | |
const getState = () => { | |
'worklet'; | |
// @ts-ignore | |
// eslint-disable-next-line no-undef | |
if (_WORKLET) { | |
return sv.value; | |
} | |
return state; | |
}; | |
return [getState, setMixedState] as const; | |
} | |
export interface LayoutInfo { | |
x: number; | |
y: number; | |
width: number; | |
height: number; | |
pageX: number; | |
pageY: number; | |
} | |
export type Layout<T> = [number, T, LayoutInfo]; | |
export type Layouts<T> = Layout<T>[]; | |
export interface MeasureLayoutProps<P, T> { | |
propsList: P[]; | |
componentRef: T; | |
onFinish(layouts: Layouts<P>): void; | |
} | |
export const MeasureLayout = <P extends {}, T extends React.ComponentType<P>>({ | |
propsList, | |
componentRef, | |
onFinish | |
}: MeasureLayoutProps<P, T>) => { | |
const viewRef = useAnimatedRef(); | |
const [getIndex, setIndex] = useAnimatedState(0); | |
const [getIsOnLayoutCalled, setIsOnLayoutCalled] = useAnimatedState(false); | |
const [getLayouts, setLayouts] = useAnimatedState<Layouts<P>>([]); | |
const ChildComponent = componentRef as React.ComponentType<P>; | |
console.log( | |
`[MeasureLayout] length [${propsList.length}], propsList: ${JSON.stringify( | |
propsList | |
)}` | |
); | |
const jsIndex = getIndex(); | |
console.log( | |
`[MeasureLayout] index [${jsIndex}], props: ${JSON.stringify( | |
propsList[jsIndex] | |
)}` | |
); | |
const onLayoutCallback = useCallback( | |
(_: LayoutChangeEvent) => { | |
console.log('onLayoutCallback: called'); | |
setIsOnLayoutCalled(true); | |
}, | |
[setIsOnLayoutCalled] | |
); | |
function runOnUIWorklet() { | |
'worklet'; | |
console.log('worklet start'); | |
const onLayoutWasCalled = getIsOnLayoutCalled(); | |
if (onLayoutWasCalled) { | |
const index = getIndex(); | |
if (index < propsList.length) { | |
const layoutInfo: LayoutInfo = measure(viewRef); | |
const layout: Layout<P> = [index, propsList[index], layoutInfo]; | |
console.log( | |
`worklet: index [${index}], measured layout: ${JSON.stringify( | |
layout | |
)}` | |
); | |
// FIXME: Doesn't work | |
// setLayouts([...getLayouts(), layout]); | |
setIndex(index + 1); | |
} else { | |
// Here return final measured layouts | |
console.log(`worklet: onFinish is called; index is ${index}`); | |
onFinish && onFinish(getLayouts()); | |
} | |
} | |
} | |
useEffect(() => { | |
console.log('useEffect: before runOnUI'); | |
runOnUI(runOnUIWorklet)(); | |
console.log('useEffect: after runOnUI'); | |
}); | |
return ( | |
<Animated.View ref={viewRef} onLayout={onLayoutCallback}> | |
{jsIndex < propsList.length && <ChildComponent {...propsList[jsIndex]} />} | |
</Animated.View> | |
); | |
}; | |
interface TextComponentProps { | |
message: string; | |
} | |
export const RunLayoutMeasure = () => { | |
const TextComponent = (props: TextComponentProps) => ( | |
<Text>{props.message}</Text> | |
); | |
const propsList: TextComponentProps[] = [ | |
{ message: '1' }, | |
{ message: '2' }, | |
{ message: '3' }, | |
{ message: '4' }, | |
{ message: '5' } | |
]; | |
return ( | |
<MeasureLayout | |
propsList={propsList} | |
componentRef={TextComponent} | |
onFinish={(layouts) => { | |
console.log( | |
`measures: ${layouts}, isArray? ${Array.isArray(layouts)}, length ${ | |
layouts.length | |
}` | |
); | |
}} | |
/> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment