mirror of
https://github.com/zhigang1992/react-native-bottom-sheet.git
synced 2026-04-28 12:15:39 +08:00
chore: added useReactiveValue/s (#65)
* chore: added useReactiveVlaue/s * chore: updated basic example
This commit is contained in:
@@ -481,4 +481,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: aff15ad1e9bd5c910cdc2b575487ce8c56864ae7
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
COCOAPODS: 1.9.3
|
||||
|
||||
@@ -1,40 +1,32 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { useHeaderHeight } from '@react-navigation/stack';
|
||||
import Animated, {
|
||||
interpolate,
|
||||
concat,
|
||||
Extrapolate,
|
||||
} from 'react-native-reanimated';
|
||||
import { concat } from 'react-native-reanimated';
|
||||
import { ReText, useValue } from 'react-native-redash';
|
||||
import BottomSheet, { BottomSheetFlatList } from '@gorhom/bottom-sheet';
|
||||
import Handle from '../../components/handle';
|
||||
import BottomSheet from '@gorhom/bottom-sheet';
|
||||
import Button from '../../components/button';
|
||||
import ContactList from '../../components/contactList';
|
||||
import { useSafeArea } from 'react-native-safe-area-context';
|
||||
|
||||
const BasicExample = () => {
|
||||
// state
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
const [dynamicSnapPoint, setDynamicSnapPoint] = useState(450);
|
||||
|
||||
// hooks
|
||||
const bottomSheetRef = useRef<BottomSheet>(null);
|
||||
const headerHeight = useHeaderHeight();
|
||||
const { top: topSafeArea } = useSafeArea();
|
||||
|
||||
// variables
|
||||
const snapPoints = useMemo(() => [150, 300, 450], []);
|
||||
const snapPoints = useMemo(() => [150, dynamicSnapPoint], [dynamicSnapPoint]);
|
||||
const position = useValue<number>(0);
|
||||
|
||||
// styles
|
||||
const shadowOverlayStyle = useMemo(
|
||||
const containerStyle = useMemo(
|
||||
() => ({
|
||||
...styles.shadowOverlay,
|
||||
opacity: interpolate(position, {
|
||||
inputRange: [300, 450],
|
||||
outputRange: [0, 1],
|
||||
extrapolate: Extrapolate.CLAMP,
|
||||
}),
|
||||
...styles.container,
|
||||
paddingTop: topSafeArea,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
[topSafeArea]
|
||||
);
|
||||
|
||||
// callbacks
|
||||
@@ -44,31 +36,20 @@ const BasicExample = () => {
|
||||
const handleSnapPress = useCallback(index => {
|
||||
bottomSheetRef.current?.snapTo(index);
|
||||
}, []);
|
||||
|
||||
const handleClosePress = useCallback(() => {
|
||||
bottomSheetRef.current?.close();
|
||||
}, []);
|
||||
const handleIncreaseDynamicSnapPoint = useCallback(() => {
|
||||
setDynamicSnapPoint(state => state + 50);
|
||||
}, []);
|
||||
|
||||
// renders
|
||||
// const renderHeader = useCallback(() => {
|
||||
// return (
|
||||
// <View style={styles.headerContainer}>
|
||||
// <Text style={styles.title}>Basic Screen</Text>
|
||||
// </View>
|
||||
// );
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={containerStyle}>
|
||||
<Button
|
||||
label="Snap To 450"
|
||||
label="Increase Dynamic Snap Point"
|
||||
style={styles.buttonContainer}
|
||||
onPress={() => handleSnapPress(2)}
|
||||
/>
|
||||
<Button
|
||||
label="Snap To 300"
|
||||
style={styles.buttonContainer}
|
||||
onPress={() => handleSnapPress(1)}
|
||||
onPress={handleIncreaseDynamicSnapPoint}
|
||||
/>
|
||||
<Button
|
||||
label="Snap To 150"
|
||||
@@ -87,92 +68,16 @@ const BasicExample = () => {
|
||||
onPress={() => setEnabled(state => !state)}
|
||||
/>
|
||||
<ReText text={concat('Position from bottom: ', position)} />
|
||||
<Animated.View pointerEvents="none" style={shadowOverlayStyle} />
|
||||
<BottomSheet
|
||||
ref={bottomSheetRef}
|
||||
enabled={enabled}
|
||||
snapPoints={snapPoints}
|
||||
initialSnapIndex={1}
|
||||
handleComponent={Handle}
|
||||
topInset={headerHeight}
|
||||
topInset={topSafeArea}
|
||||
animatedPosition={position}
|
||||
onChange={handleSheetChanges}
|
||||
>
|
||||
{/* <View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: (25 * sheetHeight) / 100,
|
||||
backgroundColor: 'rgba(0,0,0,0.25)',
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: (50 * sheetHeight) / 100,
|
||||
backgroundColor: 'rgba(0,0,0,0.50)',
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: windowWidth / 2 - 25,
|
||||
width: 50,
|
||||
height: 10,
|
||||
backgroundColor: 'red',
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: windowWidth / 2 - 25,
|
||||
bottom: 0,
|
||||
width: 50,
|
||||
height: 10,
|
||||
backgroundColor: 'red',
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: sheetHeight / 2 - 25,
|
||||
width: 10,
|
||||
height: 50,
|
||||
backgroundColor: 'red',
|
||||
}}
|
||||
/>
|
||||
|
||||
<View
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: sheetHeight / 2 - 25,
|
||||
right: 0,
|
||||
width: 10,
|
||||
height: 50,
|
||||
backgroundColor: 'red',
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* <Button
|
||||
label="Open"
|
||||
style={styles.buttonContainer}
|
||||
onPress={() => handleSnapPress(1)}
|
||||
/> */}
|
||||
{/* <ContactList type="ScrollView" header={renderHeader} /> */}
|
||||
<View style={{ height: 100, backgroundColor: 'blue' }} />
|
||||
<BottomSheetFlatList
|
||||
contentContainerStyle={{ flexGrow: 1, backgroundColor: '#fff' }}
|
||||
data={[0, 1, 2, 3, 4, 5, 6, 7, 8]}
|
||||
renderItem={() => (
|
||||
<View
|
||||
style={{ backgroundColor: 'red', height: 100, marginBottom: 20 }}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<View style={{ height: 100, backgroundColor: 'green' }} />
|
||||
<ContactList type="View" />
|
||||
</BottomSheet>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ import { State } from 'react-native-gesture-handler';
|
||||
import { useClock, useValue, snapPoint } from 'react-native-redash';
|
||||
import type { BottomSheetAnimationConfigs } from './types';
|
||||
import { GESTURE } from '../../constants';
|
||||
import { useReactiveValue, useReactiveValues } from '../../hooks';
|
||||
|
||||
interface TransitionProps extends Required<BottomSheetAnimationConfigs> {
|
||||
contentPanGestureState: Animated.Value<State>;
|
||||
@@ -48,11 +49,12 @@ export const useTransition = ({
|
||||
handlePanGestureTranslationY,
|
||||
handlePanGestureVelocityY,
|
||||
scrollableContentOffsetY,
|
||||
snapPoints,
|
||||
snapPoints: _snapPoints,
|
||||
initialPosition,
|
||||
}: TransitionProps) => {
|
||||
const currentGesture = useValue<GESTURE>(GESTURE.UNDETERMINED);
|
||||
const currentPosition = useValue(initialPosition);
|
||||
const currentPosition = useReactiveValue(initialPosition);
|
||||
const snapPoints = useReactiveValues(_snapPoints);
|
||||
|
||||
const isPanningContent = useMemo(
|
||||
() => eq(contentPanGestureState, State.ACTIVE),
|
||||
@@ -86,11 +88,13 @@ export const useTransition = ({
|
||||
frameTime: new Animated.Value(0),
|
||||
time: new Animated.Value(0),
|
||||
}),
|
||||
[initialPosition]
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
const finishTiming = useMemo(
|
||||
() => [
|
||||
// debug('finish timing', config.toValue),
|
||||
set(currentGesture, GESTURE.UNDETERMINED),
|
||||
set(shouldAnimate, 0),
|
||||
set(currentPosition, config.toValue),
|
||||
|
||||
@@ -2,3 +2,5 @@ export { useBottomSheetInternal } from './useBottomSheetInternal';
|
||||
export { useScrollable } from './useScrollable';
|
||||
export { useScrollableInternal } from './useScrollableInternal';
|
||||
export { useStableCallback } from './useStableCallback';
|
||||
export { useReactiveValue } from './useReactiveValue';
|
||||
export { useReactiveValues } from './useReactiveValues';
|
||||
|
||||
20
src/hooks/useReactiveValue.ts
Normal file
20
src/hooks/useReactiveValue.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
export const useReactiveValue = (value: number) => {
|
||||
// ref
|
||||
const ref = useRef<Animated.Value<number>>(null);
|
||||
if (ref.current === null) {
|
||||
// @ts-ignore
|
||||
ref.current = new Animated.Value(value);
|
||||
}
|
||||
|
||||
// effects
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
ref.current.setValue(value);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
return ref.current;
|
||||
};
|
||||
47
src/hooks/useReactiveValues.ts
Normal file
47
src/hooks/useReactiveValues.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
export const useReactiveValues = (values: number[]) => {
|
||||
// ref
|
||||
const ref = useRef<Animated.Value<number>[]>(null);
|
||||
if (ref.current === null) {
|
||||
// @ts-ignore
|
||||
ref.current = [];
|
||||
values.map(value => {
|
||||
// @ts-ignore
|
||||
ref.current.push(new Animated.Value(value));
|
||||
});
|
||||
}
|
||||
|
||||
// effects
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
values.map((value, index) => {
|
||||
// @ts-ignore
|
||||
if (ref.current[index]) {
|
||||
// update current value
|
||||
// @ts-ignore
|
||||
ref.current[index].setValue(value);
|
||||
} else {
|
||||
// insert current value
|
||||
// @ts-ignore
|
||||
ref.current.push(new Animated.Value(value));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* if previous animated array has more values than the updated
|
||||
* array, we will need to set the extra values to the last
|
||||
* value of the updated array.
|
||||
*/
|
||||
if (values.length < ref.current.length) {
|
||||
const lastValue = values[values.length - 1];
|
||||
for (let i = values.length - 1; i <= ref.current.length - 1; i++) {
|
||||
ref.current[i].setValue(lastValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [values]);
|
||||
|
||||
return ref.current!;
|
||||
};
|
||||
Reference in New Issue
Block a user