react-native-refresh-list2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +67 -0
  3. package/lib/module/Loading/Loading.js +19 -0
  4. package/lib/module/Loading/Loading.js.map +1 -0
  5. package/lib/module/Loading/index.js +5 -0
  6. package/lib/module/Loading/index.js.map +1 -0
  7. package/lib/module/RefreshControl/BottomContainer.js +88 -0
  8. package/lib/module/RefreshControl/BottomContainer.js.map +1 -0
  9. package/lib/module/RefreshControl/NormalControl.js +94 -0
  10. package/lib/module/RefreshControl/NormalControl.js.map +1 -0
  11. package/lib/module/RefreshControl/RefreshContainer.js +53 -0
  12. package/lib/module/RefreshControl/RefreshContainer.js.map +1 -0
  13. package/lib/module/RefreshControl/RefreshContext.js +32 -0
  14. package/lib/module/RefreshControl/RefreshContext.js.map +1 -0
  15. package/lib/module/RefreshControl/RefreshFlatList.js +291 -0
  16. package/lib/module/RefreshControl/RefreshFlatList.js.map +1 -0
  17. package/lib/module/RefreshControl/RefreshScrollView.js +279 -0
  18. package/lib/module/RefreshControl/RefreshScrollView.js.map +1 -0
  19. package/lib/module/RefreshControl/index.js +7 -0
  20. package/lib/module/RefreshControl/index.js.map +1 -0
  21. package/lib/module/RefreshControl/type.js +39 -0
  22. package/lib/module/RefreshControl/type.js.map +1 -0
  23. package/lib/module/icon/CommentIcon.js +32 -0
  24. package/lib/module/icon/CommentIcon.js.map +1 -0
  25. package/lib/module/icon/Icon.js +25 -0
  26. package/lib/module/icon/Icon.js.map +1 -0
  27. package/lib/module/icon/MoreIcon.js +40 -0
  28. package/lib/module/icon/MoreIcon.js.map +1 -0
  29. package/lib/module/icon/Praise.js +23 -0
  30. package/lib/module/icon/Praise.js.map +1 -0
  31. package/lib/module/icon/SearchIcon.js +35 -0
  32. package/lib/module/icon/SearchIcon.js.map +1 -0
  33. package/lib/module/icon/index.js +5 -0
  34. package/lib/module/icon/index.js.map +1 -0
  35. package/lib/module/icon/library.js +20 -0
  36. package/lib/module/icon/library.js.map +1 -0
  37. package/lib/module/index.js +4 -0
  38. package/lib/module/index.js.map +1 -0
  39. package/lib/module/package.json +1 -0
  40. package/lib/typescript/package.json +1 -0
  41. package/lib/typescript/src/Loading/Loading.d.ts +9 -0
  42. package/lib/typescript/src/Loading/Loading.d.ts.map +1 -0
  43. package/lib/typescript/src/Loading/index.d.ts +3 -0
  44. package/lib/typescript/src/Loading/index.d.ts.map +1 -0
  45. package/lib/typescript/src/RefreshControl/BottomContainer.d.ts +17 -0
  46. package/lib/typescript/src/RefreshControl/BottomContainer.d.ts.map +1 -0
  47. package/lib/typescript/src/RefreshControl/NormalControl.d.ts +20 -0
  48. package/lib/typescript/src/RefreshControl/NormalControl.d.ts.map +1 -0
  49. package/lib/typescript/src/RefreshControl/RefreshContainer.d.ts +17 -0
  50. package/lib/typescript/src/RefreshControl/RefreshContainer.d.ts.map +1 -0
  51. package/lib/typescript/src/RefreshControl/RefreshContext.d.ts +9 -0
  52. package/lib/typescript/src/RefreshControl/RefreshContext.d.ts.map +1 -0
  53. package/lib/typescript/src/RefreshControl/RefreshFlatList.d.ts +20 -0
  54. package/lib/typescript/src/RefreshControl/RefreshFlatList.d.ts.map +1 -0
  55. package/lib/typescript/src/RefreshControl/RefreshScrollView.d.ts +20 -0
  56. package/lib/typescript/src/RefreshControl/RefreshScrollView.d.ts.map +1 -0
  57. package/lib/typescript/src/RefreshControl/index.d.ts +5 -0
  58. package/lib/typescript/src/RefreshControl/index.d.ts.map +1 -0
  59. package/lib/typescript/src/RefreshControl/type.d.ts +70 -0
  60. package/lib/typescript/src/RefreshControl/type.d.ts.map +1 -0
  61. package/lib/typescript/src/icon/CommentIcon.d.ts +3 -0
  62. package/lib/typescript/src/icon/CommentIcon.d.ts.map +1 -0
  63. package/lib/typescript/src/icon/Icon.d.ts +10 -0
  64. package/lib/typescript/src/icon/Icon.d.ts.map +1 -0
  65. package/lib/typescript/src/icon/MoreIcon.d.ts +3 -0
  66. package/lib/typescript/src/icon/MoreIcon.d.ts.map +1 -0
  67. package/lib/typescript/src/icon/Praise.d.ts +9 -0
  68. package/lib/typescript/src/icon/Praise.d.ts.map +1 -0
  69. package/lib/typescript/src/icon/SearchIcon.d.ts +9 -0
  70. package/lib/typescript/src/icon/SearchIcon.d.ts.map +1 -0
  71. package/lib/typescript/src/icon/index.d.ts +3 -0
  72. package/lib/typescript/src/icon/index.d.ts.map +1 -0
  73. package/lib/typescript/src/icon/library.d.ts +17 -0
  74. package/lib/typescript/src/icon/library.d.ts.map +1 -0
  75. package/lib/typescript/src/index.d.ts +2 -0
  76. package/lib/typescript/src/index.d.ts.map +1 -0
  77. package/package.json +168 -0
  78. package/src/Loading/Loading.tsx +15 -0
  79. package/src/Loading/index.tsx +5 -0
  80. package/src/RefreshControl/BottomContainer.tsx +112 -0
  81. package/src/RefreshControl/NormalControl.tsx +118 -0
  82. package/src/RefreshControl/RefreshContainer.tsx +74 -0
  83. package/src/RefreshControl/RefreshContext.tsx +30 -0
  84. package/src/RefreshControl/RefreshFlatList.tsx +372 -0
  85. package/src/RefreshControl/RefreshScrollView.tsx +359 -0
  86. package/src/RefreshControl/index.tsx +5 -0
  87. package/src/RefreshControl/type.ts +74 -0
  88. package/src/icon/CommentIcon.tsx +29 -0
  89. package/src/icon/Icon.tsx +26 -0
  90. package/src/icon/MoreIcon.tsx +38 -0
  91. package/src/icon/Praise.tsx +21 -0
  92. package/src/icon/SearchIcon.tsx +36 -0
  93. package/src/icon/index.tsx +3 -0
  94. package/src/icon/library.ts +30 -0
  95. package/src/index.tsx +1 -0
package/package.json ADDED
@@ -0,0 +1,168 @@
1
+ {
2
+ "name": "react-native-refresh-list2",
3
+ "version": "1.0.0",
4
+ "description": "reactnative 下拉刷新统一android/ios 样式ios样式",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "!ios/build",
19
+ "!android/build",
20
+ "!android/gradle",
21
+ "!android/gradlew",
22
+ "!android/gradlew.bat",
23
+ "!android/local.properties",
24
+ "!**/__tests__",
25
+ "!**/__fixtures__",
26
+ "!**/__mocks__",
27
+ "!**/.*"
28
+ ],
29
+ "scripts": {
30
+ "example": "yarn workspace react-native-refresh-flatlist-example",
31
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
32
+ "prepare": "bob build",
33
+ "typecheck": "tsc",
34
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
35
+ "test": "jest",
36
+ "release": "release-it --only-version"
37
+ },
38
+ "keywords": [
39
+ "react-native",
40
+ "ios",
41
+ "android"
42
+ ],
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/1176506184/react-native-refresh-flatlist.git"
46
+ },
47
+ "author": "tangfeifan <1050069968@qq.com> (https://github.com/1176506184/react-native-refresh-flatlist)",
48
+ "license": "MIT",
49
+ "bugs": {
50
+ "url": "https://github.com/1176506184/react-native-refresh-flatlist/issues"
51
+ },
52
+ "homepage": "https://github.com/1176506184/react-native-refresh-flatlist#readme",
53
+ "publishConfig": {
54
+ "registry": "https://registry.npmjs.org/"
55
+ },
56
+ "devDependencies": {
57
+ "@babel/core": "^7.25.2",
58
+ "@commitlint/config-conventional": "^19.8.1",
59
+ "@eslint/compat": "^1.3.2",
60
+ "@eslint/eslintrc": "^3.3.1",
61
+ "@eslint/js": "^9.35.0",
62
+ "@react-native/babel-preset": "0.83.0",
63
+ "@react-native/eslint-config": "0.83.0",
64
+ "@release-it/conventional-changelog": "^10.0.1",
65
+ "@types/jest": "^29.5.14",
66
+ "@types/react": "^19.2.0",
67
+ "commitlint": "^19.8.1",
68
+ "del-cli": "^6.0.0",
69
+ "eslint": "^9.35.0",
70
+ "eslint-config-prettier": "^10.1.8",
71
+ "eslint-plugin-prettier": "^5.5.4",
72
+ "jest": "^29.7.0",
73
+ "lefthook": "^2.0.3",
74
+ "prettier": "^2.8.8",
75
+ "react": "19.2.0",
76
+ "react-native": "0.83.0",
77
+ "react-native-builder-bob": "^0.40.13",
78
+ "react-native-gesture-handler": "^2.30.0",
79
+ "react-native-reanimated": "^4.1.0",
80
+ "react-native-svg": "^15.15.3",
81
+ "react-native-worklets": "0.7",
82
+ "release-it": "^19.0.4",
83
+ "turbo": "^2.5.6",
84
+ "typescript": "^5.9.2"
85
+ },
86
+ "peerDependencies": {
87
+ "react": "*",
88
+ "react-native": "*",
89
+ "react-native-gesture-handler": "*",
90
+ "react-native-reanimated": "^4.1.0",
91
+ "react-native-svg": "*",
92
+ "react-native-worklets": "0.7"
93
+ },
94
+ "workspaces": [
95
+ "example"
96
+ ],
97
+ "packageManager": "yarn@4.11.0",
98
+ "react-native-builder-bob": {
99
+ "source": "src",
100
+ "output": "lib",
101
+ "targets": [
102
+ [
103
+ "module",
104
+ {
105
+ "esm": true
106
+ }
107
+ ],
108
+ [
109
+ "typescript",
110
+ {
111
+ "project": "tsconfig.build.json"
112
+ }
113
+ ]
114
+ ]
115
+ },
116
+ "prettier": {
117
+ "quoteProps": "consistent",
118
+ "singleQuote": true,
119
+ "tabWidth": 2,
120
+ "trailingComma": "es5",
121
+ "useTabs": false
122
+ },
123
+ "jest": {
124
+ "preset": "react-native",
125
+ "modulePathIgnorePatterns": [
126
+ "<rootDir>/example/node_modules",
127
+ "<rootDir>/lib/"
128
+ ]
129
+ },
130
+ "commitlint": {
131
+ "extends": [
132
+ "@commitlint/config-conventional"
133
+ ]
134
+ },
135
+ "release-it": {
136
+ "git": {
137
+ "commitMessage": "chore: release ${version}",
138
+ "tagName": "v${version}"
139
+ },
140
+ "npm": {
141
+ "publish": true
142
+ },
143
+ "github": {
144
+ "release": true
145
+ },
146
+ "plugins": {
147
+ "@release-it/conventional-changelog": {
148
+ "preset": {
149
+ "name": "angular"
150
+ }
151
+ }
152
+ }
153
+ },
154
+ "create-react-native-library": {
155
+ "type": "turbo-module",
156
+ "languages": "kotlin-objc",
157
+ "tools": [
158
+ "eslint",
159
+ "jest",
160
+ "lefthook",
161
+ "release-it"
162
+ ],
163
+ "version": "0.57.2"
164
+ },
165
+ "resolutions": {
166
+ "semver": "7.6.3"
167
+ }
168
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { ActivityIndicator } from 'react-native';
3
+
4
+ export interface LoadingProps {
5
+ color?: string;
6
+ size?: 'small' | 'large';
7
+ animating?: boolean;
8
+ }
9
+
10
+ const Loading: React.FC<LoadingProps> = (props) => {
11
+ const { color, size, animating = true } = props;
12
+ return <ActivityIndicator {...{ color, size, animating }} />;
13
+ };
14
+
15
+ export default Loading;
@@ -0,0 +1,5 @@
1
+ import Loading from './Loading';
2
+
3
+ export {
4
+ Loading,
5
+ };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Refresh Control Implement
3
+ * Every Control can get props from useRefresh()
4
+ */
5
+ import React, {useState} from 'react';
6
+ import {View, Text, StyleSheet} from 'react-native';
7
+ import Animated, {
8
+ useAnimatedStyle,
9
+ useAnimatedReaction,
10
+ runOnJS,
11
+ withTiming,
12
+ useSharedValue,
13
+ } from 'react-native-reanimated';
14
+ import {RefreshStatus, useRefreshScroll} from './type';
15
+ import {Icon} from '../icon';
16
+ import {Loading} from '../Loading';
17
+
18
+
19
+ interface NormalControlProps {
20
+ textConfig?: {
21
+ normal: string;
22
+ release: string;
23
+ refresing: string;
24
+ done: string;
25
+ };
26
+ position: 'top' | 'bottom';
27
+ }
28
+
29
+ const NormalControl: React.FC<NormalControlProps> = (props) => {
30
+ const {
31
+ textConfig = {
32
+ normal: '下拉刷新',
33
+ release: '释放以刷新',
34
+ refresing: '努力刷新中',
35
+ done: '刷新成功',
36
+ },
37
+ position,
38
+ } = props;
39
+ const {transitionY, triggleHeight, refreshStatus} = useRefreshScroll();
40
+ const [refreshText, setRefreshText] = useState(textConfig.normal);
41
+ const [loading, setLoading] = useState(false);
42
+ const degree = useSharedValue(0);
43
+
44
+ const setRefreshTextByStatus = (text: string) => {
45
+ setRefreshText(text);
46
+ };
47
+
48
+ useAnimatedReaction(
49
+ () => refreshStatus.value,
50
+ (value) => {
51
+ if (value === RefreshStatus.Idle) {
52
+ runOnJS(setRefreshTextByStatus)(textConfig.normal);
53
+ }
54
+ if (value === RefreshStatus.Pulling) {
55
+ runOnJS(setRefreshTextByStatus)(textConfig.normal);
56
+ }
57
+ if (value === RefreshStatus.Reached) {
58
+ runOnJS(setRefreshTextByStatus)(textConfig.release);
59
+ }
60
+ if (value === RefreshStatus.Holding) {
61
+ runOnJS(setRefreshTextByStatus)(textConfig.refresing);
62
+ runOnJS(setLoading)(true);
63
+ }
64
+ if (value === RefreshStatus.Done) {
65
+ runOnJS(setRefreshTextByStatus)(textConfig.done);
66
+ runOnJS(setLoading)(false);
67
+ }
68
+ }
69
+ );
70
+
71
+ const arrowStyle = useAnimatedStyle(() => {
72
+ degree.value = withTiming(transitionY.value >= triggleHeight ? 180 : 0, {
73
+ duration: 200,
74
+ });
75
+
76
+ return {
77
+ transform: [
78
+ {
79
+ rotateZ: `${degree.value}deg`,
80
+ },
81
+ ],
82
+ };
83
+ });
84
+
85
+ return (
86
+ <View style={styles.container}>
87
+ {loading ? (
88
+ <Loading/>
89
+ ) : (
90
+ <Animated.View style={arrowStyle}>
91
+ <Icon
92
+ name={position === 'top' ? 'arrow-line-down' : 'arrow-line-up'}
93
+ size={18}
94
+ color={'grey'}
95
+ />
96
+ </Animated.View>
97
+ )}
98
+ <Text style={styles.textStyle}>{refreshText}</Text>
99
+ </View>
100
+ );
101
+ };
102
+
103
+ const styles = StyleSheet.create({
104
+ container: {
105
+ flexDirection: 'row',
106
+ },
107
+ textStyle: {
108
+ marginHorizontal: 10,
109
+ },
110
+ });
111
+
112
+ export default NormalControl;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Refresh Control Implement
3
+ * Every Control can get props from useRefresh()
4
+ */
5
+ import React, { useState } from 'react';
6
+ import { View, StyleSheet, Text } from 'react-native';
7
+ import Animated, {
8
+ useAnimatedStyle,
9
+ useAnimatedReaction,
10
+ runOnJS,
11
+ withTiming,
12
+ useSharedValue,
13
+ } from 'react-native-reanimated';
14
+ import { RefreshStatus, useRefreshScroll } from './type';
15
+ import { Icon } from '../icon';
16
+ import { Loading } from '../Loading';
17
+
18
+ interface NormalControlProps {
19
+ textConfig?: {
20
+ normal: string;
21
+ release: string;
22
+ refresing: string;
23
+ done: string;
24
+ };
25
+ position: 'top' | 'bottom';
26
+ textStyle?: any;
27
+ iconColor?: string;
28
+ arrowColor?: string;
29
+ }
30
+
31
+ const NormalControl: React.FC<NormalControlProps> = (props) => {
32
+ const {
33
+ textConfig = {
34
+ normal: '下拉刷新',
35
+ release: '释放以刷新',
36
+ refresing: '努力刷新中',
37
+ done: '刷新成功',
38
+ },
39
+ position,
40
+ } = props;
41
+ const { transitionY, triggleHeight, refreshStatus } = useRefreshScroll();
42
+ const [refreshText, setRefreshText] = useState(textConfig.normal);
43
+ const [loading, setLoading] = useState(false);
44
+ const degree = useSharedValue(0);
45
+ const iconColor = props.iconColor || '#7A6FFA';
46
+ const arrowColor = props.arrowColor || 'grey';
47
+
48
+ const setRefreshTextByStatus = (text: string) => {
49
+ setRefreshText(text);
50
+ };
51
+
52
+ useAnimatedReaction(
53
+ () => refreshStatus.value,
54
+ (value) => {
55
+ if (value === RefreshStatus.Idle) {
56
+ runOnJS(setRefreshTextByStatus)(textConfig.normal);
57
+ }
58
+ if (value === RefreshStatus.Pulling) {
59
+ runOnJS(setRefreshTextByStatus)(textConfig.normal);
60
+ }
61
+ if (value === RefreshStatus.Reached) {
62
+ runOnJS(setRefreshTextByStatus)(textConfig.release);
63
+ }
64
+ if (value === RefreshStatus.Holding) {
65
+ runOnJS(setRefreshTextByStatus)(textConfig.refresing);
66
+ runOnJS(setLoading)(true);
67
+ }
68
+ if (value === RefreshStatus.Done) {
69
+ console.log('done');
70
+ runOnJS(setRefreshTextByStatus)(textConfig.done);
71
+ runOnJS(setLoading)(false);
72
+ }
73
+ }
74
+ );
75
+
76
+ const arrowStyle = useAnimatedStyle(() => {
77
+ degree.value = withTiming(transitionY.value >= triggleHeight ? 180 : 0, {
78
+ duration: 200,
79
+ });
80
+
81
+ return {
82
+ transform: [
83
+ {
84
+ rotateZ: `${degree.value}deg`,
85
+ },
86
+ ],
87
+ };
88
+ });
89
+
90
+ return (
91
+ <View style={styles.container}>
92
+ {loading ? (
93
+ <Loading color={iconColor} />
94
+ ) : (
95
+ <Animated.View style={arrowStyle}>
96
+ <Icon
97
+ name={position === 'top' ? 'arrow-line-down' : 'arrow-line-up'}
98
+ size={18}
99
+ color={arrowColor}
100
+ />
101
+ </Animated.View>
102
+ )}
103
+ <Text style={[styles.textStyle, props.textStyle]}>{refreshText}</Text>
104
+ </View>
105
+ );
106
+ };
107
+
108
+ const styles = StyleSheet.create({
109
+ container: {
110
+ flexDirection: 'row',
111
+ alignItems: 'center'
112
+ },
113
+ textStyle: {
114
+ marginHorizontal: 10,
115
+ },
116
+ });
117
+
118
+ export default NormalControl;
@@ -0,0 +1,74 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ import { StyleSheet, Dimensions } from 'react-native';
3
+ import Animated, {
4
+ interpolate,
5
+ useAnimatedStyle,
6
+ } from 'react-native-reanimated';
7
+ import type { SharedValue } from 'react-native-reanimated';
8
+ import { RefreshStatus } from './type';
9
+
10
+ const { width } = Dimensions.get('window');
11
+
12
+ interface RefreshContainerContextProps extends RefreshContainerProps {
13
+ }
14
+
15
+ export const RefreshContainerContext =
16
+ createContext<RefreshContainerContextProps>(
17
+ {} as RefreshContainerContextProps
18
+ );
19
+ export const useRefresh = () => useContext(RefreshContainerContext);
20
+
21
+ interface RefreshContainerProps {
22
+ transitionY: SharedValue<number>;
23
+ triggleHeight: number; // 当下拉距离超过该值,触发下拉刷新方法
24
+ refreshStatus: SharedValue<RefreshStatus>;
25
+ children: React.ReactNode;
26
+ offsetTop?: number;
27
+ }
28
+
29
+ const RefreshContainer: React.FC<RefreshContainerProps> = (props) => {
30
+ const { children, transitionY, triggleHeight, refreshStatus } = props;
31
+
32
+ const animatedStyle = useAnimatedStyle(() => {
33
+ if (transitionY.value <= (0 + (props.offsetTop || 0))) {
34
+ return {
35
+ opacity: 0,
36
+ height: 0
37
+ };
38
+ }
39
+ return {
40
+ height: transitionY.value,
41
+ opacity: interpolate(
42
+ transitionY.value,
43
+ [0, triggleHeight / 3, triggleHeight],
44
+ [0, 0, 1]
45
+ ),
46
+ };
47
+ });
48
+
49
+ return (
50
+ <RefreshContainerContext.Provider
51
+ // @ts-ignore
52
+ value={{
53
+ transitionY,
54
+ triggleHeight,
55
+ refreshStatus,
56
+ }}
57
+ >
58
+ <Animated.View style={[styles.container, animatedStyle]}>
59
+ {children}
60
+ </Animated.View>
61
+ </RefreshContainerContext.Provider>
62
+ );
63
+ };
64
+
65
+ const styles = StyleSheet.create({
66
+ container: {
67
+ width,
68
+ alignItems: 'center',
69
+ justifyContent: 'center',
70
+ position: 'absolute',
71
+ },
72
+ });
73
+
74
+ export default RefreshContainer;
@@ -0,0 +1,30 @@
1
+ import React, {createContext, useContext, useState} from 'react';
2
+
3
+ // 定义上下文类型
4
+ interface RefreshContextType {
5
+ isRefreshing: boolean;
6
+ setIsRefreshing: (value: boolean) => void;
7
+ }
8
+
9
+ // 创建上下文
10
+ const RefreshContext = createContext<RefreshContextType | undefined>(undefined);
11
+
12
+ // 提供上下文的 Provider 组件
13
+ export const RefreshProvider: React.FC = ({children}: any) => {
14
+ const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
15
+
16
+ return (
17
+ <RefreshContext.Provider value={{isRefreshing, setIsRefreshing}}>
18
+ {children}
19
+ </RefreshContext.Provider>
20
+ );
21
+ };
22
+
23
+ // 自定义 Hook 用于消费上下文
24
+ export const useRefresh = (): RefreshContextType => {
25
+ const context = useContext(RefreshContext);
26
+ if (!context) {
27
+ throw new Error('useRefresh must be used within a RefreshProvider');
28
+ }
29
+ return context;
30
+ };