Created
September 1, 2020 13:48
-
-
Save likern/40608efe9e4f2276803cc44a17146fdf to your computer and use it in GitHub Desktop.
This file contains hidden or 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 layoutInfo: LayoutInfo = measure(viewRef); | |
| console.log(`layoutInfo: ${JSON.stringify(layoutInfo)}`); | |
| } | |
| // if (!onLayoutWasCalled) { | |
| // const res = 10; | |
| // // const res = measure(viewRef); | |
| // console.log(`before onLayout call: ${viewRef()}`); | |
| // } | |
| // 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