From a7f678cc65b0563c9ab03071d52f30a46eb6d90b Mon Sep 17 00:00:00 2001 From: gronxb Date: Mon, 11 Dec 2023 16:26:16 +0900 Subject: [PATCH 1/8] feat: add PagerViewContext and usePagerView hook --- src/PagerView.tsx | 48 ++++++++++++++++++++++++--------- src/PagerViewNativeComponent.ts | 4 +++ src/index.tsx | 1 + src/usePagerView.tsx | 41 ++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 src/usePagerView.tsx diff --git a/src/PagerView.tsx b/src/PagerView.tsx index 9122b992..05162400 100644 --- a/src/PagerView.tsx +++ b/src/PagerView.tsx @@ -5,6 +5,7 @@ import type { OnPageScrollEventData, OnPageScrollStateChangedEventData, OnPageSelectedEventData, + PagerViewState, } from './PagerViewNativeComponent'; import type * as ReactNative from 'react-native'; @@ -13,6 +14,7 @@ import { childrenWithOverriddenStyle } from './utils'; import PagerViewView, { Commands as PagerViewCommands, } from './PagerViewNativeComponent'; +import { PagerViewContext } from './usePagerView'; /** * Container that allows to flip left and right between child views. Each @@ -56,10 +58,15 @@ import PagerViewView, { * ``` */ -export class PagerView extends React.Component { +export class PagerView extends React.Component { private isScrolling = false; pagerView: React.ElementRef | null = null; + constructor(props: PagerViewProps) { + super(props); + this.state = { page: props.initialPage ?? 0 }; + } + private _onPageScroll = ( e: ReactNative.NativeSyntheticEvent ) => { @@ -90,6 +97,7 @@ export class PagerView extends React.Component { if (this.props.onPageSelected) { this.props.onPageSelected(e); } + this.setState({ page: e.nativeEvent.position }); }; /** @@ -144,19 +152,33 @@ export class PagerView extends React.Component { render() { return ( - { - this.pagerView = ref; + 0, + page: this.state.page, + setPage: this.setPage, + setPageWithoutAnimation: this.setPageWithoutAnimation, + setScrollEnabled: this.setScrollEnabled, }} - style={this.props.style} - layoutDirection={this.deducedLayoutDirection} - onPageScroll={this._onPageScroll} - onPageScrollStateChanged={this._onPageScrollStateChanged} - onPageSelected={this._onPageSelected} - onMoveShouldSetResponderCapture={this._onMoveShouldSetResponderCapture} - children={childrenWithOverriddenStyle(this.props.children)} - /> + > + { + this.pagerView = ref; + }} + style={this.props.style} + layoutDirection={this.deducedLayoutDirection} + onPageScroll={this._onPageScroll} + onPageScrollStateChanged={this._onPageScrollStateChanged} + onPageSelected={this._onPageSelected} + onMoveShouldSetResponderCapture={ + this._onMoveShouldSetResponderCapture + } + children={childrenWithOverriddenStyle(this.props.children)} + /> + ); } } diff --git a/src/PagerViewNativeComponent.ts b/src/PagerViewNativeComponent.ts index f102aa96..276879ab 100644 --- a/src/PagerViewNativeComponent.ts +++ b/src/PagerViewNativeComponent.ts @@ -39,6 +39,10 @@ export interface NativeProps extends ViewProps { type PagerViewViewType = HostComponent; +export interface PagerViewState { + page: number; +} + export interface NativeCommands { setPage: ( viewRef: React.ElementRef, diff --git a/src/index.tsx b/src/index.tsx index 1e7cbe3d..56f5982e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import type * as ReactNative from 'react-native'; import { PagerView } from './PagerView'; export default PagerView; +export { usePagerView, type PagerViewMethod } from './usePagerView'; import type { OnPageScrollEventData as PagerViewOnPageScrollEventData, diff --git a/src/usePagerView.tsx b/src/usePagerView.tsx new file mode 100644 index 00000000..0c2d4470 --- /dev/null +++ b/src/usePagerView.tsx @@ -0,0 +1,41 @@ +import React, { useContext } from 'react'; + +export type PagerViewMethod = { + page: number; + hasNextPage: boolean; + hasPreviousPage: boolean; + setPage: (selectedPage: number) => void; + setPageWithoutAnimation: (selectedPage: number) => void; + setScrollEnabled: (scrollEnabled: boolean) => void; +}; + +export const PagerViewContext = React.createContext({ + page: 0, + hasNextPage: false, + hasPreviousPage: false, + setPage: () => { + throw new Error('setPage must be used within a component'); + }, + setPageWithoutAnimation: () => { + throw new Error( + 'setPageWithoutAnimation must be used within a component' + ); + }, + setScrollEnabled: () => { + throw new Error( + 'setScrollEnabled must be used within a component' + ); + }, +}); + +export const usePagerView = () => { + const value = useContext(PagerViewContext); + + if (!value) { + throw new Error( + 'usePagerView must be used within a component' + ); + } + + return value; +}; From 120d9b52ce23ceef393afb007ce0285f0b0043c3 Mon Sep 17 00:00:00 2001 From: gronxb Date: Mon, 11 Dec 2023 16:27:28 +0900 Subject: [PATCH 2/8] feat: usePagerView example --- example/src/App.tsx | 2 + example/src/UsePagerViewExample.tsx | 91 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 example/src/UsePagerViewExample.tsx diff --git a/example/src/App.tsx b/example/src/App.tsx index eca97d7c..c5418fb1 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -34,6 +34,7 @@ import ReanimatedOnPageScrollExample from './ReanimatedOnPageScrollExample'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { UsePagerViewExample } from './UsePagerViewExample'; const examples = [ { component: BasicPagerViewExample, name: 'Basic Example' }, @@ -64,6 +65,7 @@ const examples = [ name: 'Reanimated onPageScroll example', }, { component: CoverflowExample, name: 'CoverflowExample' }, + { component: UsePagerViewExample, name: 'UsePagerViewExample' }, ]; function App() { diff --git a/example/src/UsePagerViewExample.tsx b/example/src/UsePagerViewExample.tsx new file mode 100644 index 00000000..58a378ee --- /dev/null +++ b/example/src/UsePagerViewExample.tsx @@ -0,0 +1,91 @@ +import PagerView, { usePagerView } from 'react-native-pager-view'; +import React from 'react'; +import { useState } from 'react'; +import { View, StyleSheet, Button } from 'react-native'; +import { PAGES, createPage } from './utils'; +import { Text } from 'react-native'; + +const Item = () => { + const { + page, + hasNextPage, + hasPreviousPage, + setPage, + setPageWithoutAnimation, + setScrollEnabled, + } = usePagerView(); + + return ( + + Current Page: {page} + hasNextPage: {String(hasNextPage)} + hasPreviousPage: {String(hasPreviousPage)} +