diff --git a/README.md b/README.md
index f608dd4f..4aac1ac7 100644
--- a/README.md
+++ b/README.md
@@ -229,6 +229,48 @@ const pageScrollHandler = usePageScrollHandler({
;
```
+## usePager Hook Usage
+The `usePager` hook is a convenient way to manage the state and control the behavior of the `` component. It provides functions and variables to interact with the pager, such as navigating between pages and enabling/disabling scrolling.
+
+**Warning:** The usePager hook is compatible with React version 18 and above. Ensure that your project is using an appropriate version of React before implementing this hook.
+
+Below is an example of how to use the usePager hook:
+
+```jsx
+// 1. Create a component that utilizes the hook within the component..
+
+
+
+
+// 2. Inside HookComponent, use the `usePager` hook.
+const HookComponent = () => {
+ const {
+ page,
+ hasNextPage,
+ hasPreviousPage,
+ setPage,
+ setPageWithoutAnimation,
+ setScrollEnabled,
+ } = usePager();
+
+ return (
+
+ Current Page: {page}
+
+ )
+}
+
+```
+
+
## License
MIT
diff --git a/example/src/App.tsx b/example/src/App.tsx
index eca97d7c..37291afd 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 { UsePagerExample } from './UsePagerExample';
const examples = [
{ component: BasicPagerViewExample, name: 'Basic Example' },
@@ -64,6 +65,7 @@ const examples = [
name: 'Reanimated onPageScroll example',
},
{ component: CoverflowExample, name: 'CoverflowExample' },
+ { component: UsePagerExample, name: 'UsePagerExample' },
];
function App() {
diff --git a/example/src/UsePagerExample.tsx b/example/src/UsePagerExample.tsx
new file mode 100644
index 00000000..bdef00d0
--- /dev/null
+++ b/example/src/UsePagerExample.tsx
@@ -0,0 +1,96 @@
+import PagerView, { usePager } from 'react-native-pager-view';
+import React from 'react';
+import { View, StyleSheet, Button } from 'react-native';
+import { Text } from 'react-native';
+
+const NonHookComponent = () => {
+ console.log('rerender ');
+
+ return (
+
+ HasNotHook
+
+ );
+};
+
+const HookComponent = ({ index }: { index: number }) => {
+ const {
+ page,
+ hasNextPage,
+ hasPreviousPage,
+ setPage,
+ setPageWithoutAnimation,
+ setScrollEnabled,
+ } = usePager();
+
+ console.log(`rerender `);
+
+ return (
+
+ Current Page: {page}
+ hasNextPage: {String(hasNextPage)}
+ hasPreviousPage: {String(hasPreviousPage)}
+
+ );
+};
+
+export const UsePagerExample = (): JSX.Element => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ flex: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ marginVertical: 10,
+ },
+ separator: {
+ margin: 16,
+ },
+});
diff --git a/src/PagerView.tsx b/src/PagerView.tsx
index 9122b992..01156e52 100644
--- a/src/PagerView.tsx
+++ b/src/PagerView.tsx
@@ -13,6 +13,7 @@ import { childrenWithOverriddenStyle } from './utils';
import PagerViewView, {
Commands as PagerViewCommands,
} from './PagerViewNativeComponent';
+import { PagerStore, PagerViewContext, createPagerStore } from './usePager';
/**
* Container that allows to flip left and right between child views. Each
@@ -59,6 +60,12 @@ import PagerViewView, {
export class PagerView extends React.Component {
private isScrolling = false;
pagerView: React.ElementRef | null = null;
+ store: PagerStore | null = null;
+
+ constructor(props: PagerViewProps) {
+ super(props);
+ this.store = createPagerStore(props.initialPage ?? 0);
+ }
private _onPageScroll = (
e: ReactNative.NativeSyntheticEvent
@@ -90,6 +97,12 @@ export class PagerView extends React.Component {
if (this.props.onPageSelected) {
this.props.onPageSelected(e);
}
+ this.store?.setState({
+ page: e.nativeEvent.position,
+ hasNextPage:
+ e.nativeEvent.position < React.Children.count(this.props.children) - 1,
+ hasPreviousPage: e.nativeEvent.position > 0,
+ });
};
/**
@@ -144,19 +157,30 @@ export class PagerView extends React.Component {
render() {
return (
- {
- this.pagerView = ref;
+
+ >
+ {
+ 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/index.tsx b/src/index.tsx
index 1e7cbe3d..7130c2f3 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,6 +1,8 @@
import type * as ReactNative from 'react-native';
import { PagerView } from './PagerView';
export default PagerView;
+export { usePager } from './usePager';
+export type { PagerState } from './usePager';
import type {
OnPageScrollEventData as PagerViewOnPageScrollEventData,
diff --git a/src/usePager.ts b/src/usePager.ts
new file mode 100644
index 00000000..1ce823db
--- /dev/null
+++ b/src/usePager.ts
@@ -0,0 +1,71 @@
+import React, { useContext, useSyncExternalStore } from 'react';
+
+export type PagerViewContextValue = {
+ store: PagerStore | null;
+ setPage: (selectedPage: number) => void;
+ setPageWithoutAnimation: (selectedPage: number) => void;
+ setScrollEnabled: (scrollEnabled: boolean) => void;
+};
+
+export type PagerState = {
+ page: number;
+ hasNextPage: boolean;
+ hasPreviousPage: boolean;
+};
+
+export type PagerStore = {
+ getState: () => PagerState;
+ setState: (state: PagerState) => void;
+ subscribe: (listener: () => void) => () => void;
+};
+
+export const PagerViewContext =
+ React.createContext(null);
+
+export const createPagerStore = (initialPage: number) => {
+ let state: PagerState = {
+ page: initialPage,
+ hasNextPage: false,
+ hasPreviousPage: false,
+ };
+
+ const getState = () => {
+ return state;
+ };
+
+ const listeners = new Set<() => void>();
+
+ const emitChange = () => {
+ for (const listener of listeners) {
+ listener();
+ }
+ };
+
+ const setState = (newState: PagerState) => {
+ state = newState;
+ emitChange();
+ };
+
+ const subscribe = (listener: () => void) => {
+ listeners.add(listener);
+ return () => listeners.delete(listener);
+ };
+
+ return { getState, setState, subscribe };
+};
+
+export const usePager = () => {
+ if (Number(React.version.split('.')[0]) < 18) {
+ throw new Error('usePager requires React 18 or later.');
+ }
+ const value = useContext(PagerViewContext);
+
+ if (!value || !value.store) {
+ throw new Error('usePager must be used within a component');
+ }
+
+ const { store, ...methods } = value;
+
+ const state = useSyncExternalStore(store.subscribe, store.getState);
+ return { ...methods, ...state };
+};