Skip to content

Commit 09fa9ee

Browse files
committed
fix(ready): fix issue where the isReady wasn't set at first
1 parent 0268ef0 commit 09fa9ee

File tree

6 files changed

+79
-99
lines changed

6 files changed

+79
-99
lines changed

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,5 @@
161161
}
162162
]
163163
]
164-
},
165-
"dependencies": {
166-
"jotai": "^2.12.2"
167164
}
168165
}

src/Provider.tsx

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,38 @@ import {
2727
StyleSheet,
2828
useWindowDimensions,
2929
} from 'react-native';
30-
import {
31-
Provider as JotaiProvider,
32-
useAtom,
33-
useAtomValue,
34-
useSetAtom,
35-
} from 'jotai/react';
36-
import { selectAtom } from 'jotai/utils';
3730
import { Platform } from 'react-native';
3831

39-
import { elementsAtom, inputsAtom, wrapperOffsetAtom } from './state';
4032
import { useKeyboard } from './useKeyboard';
4133
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4234

35+
export type RefType = TextInput | View | Animated.View | null;
36+
37+
export type Elements = Record<
38+
string,
39+
{
40+
isFocus: boolean;
41+
position: number;
42+
height: number;
43+
name: string;
44+
}
45+
>;
46+
47+
export type InputType = Record<string, RefObject<TextInput>>;
48+
4349
const isAndroid = Platform.OS === 'android';
4450

4551
function Wrapper(props: PropsWithChildren<{}>) {
46-
const setWrapperOffsetAtom = useSetAtom(wrapperOffsetAtom);
4752
const windowDimensions = useWindowDimensions();
48-
const { wrapperRef } = useSmartScrollContext();
53+
const { wrapperRef, setWrapperOffset } = useSmartScrollContext();
4954

5055
return (
5156
<View
5257
style={styles.wrapper}
5358
ref={wrapperRef}
5459
onLayout={({ nativeEvent }) => {
5560
if (nativeEvent.layout.height < windowDimensions.height) {
56-
setWrapperOffsetAtom(
57-
windowDimensions.height - nativeEvent.layout.height
58-
);
61+
setWrapperOffset(windowDimensions.height - nativeEvent.layout.height);
5962
}
6063
}}
6164
>
@@ -72,11 +75,9 @@ const styles = StyleSheet.create({
7275

7376
export default function SmartScrollView(props: PropsWithChildren<{}>) {
7477
return (
75-
<JotaiProvider>
76-
<SmartScrollProvider>
77-
<Wrapper {...props} />
78-
</SmartScrollProvider>
79-
</JotaiProvider>
78+
<SmartScrollProvider>
79+
<Wrapper {...props} />
80+
</SmartScrollProvider>
8081
);
8182
}
8283

@@ -85,14 +86,31 @@ const SmartScrollContext = React.createContext<{
8586
scrollY: SharedValue<number>;
8687
isReady: boolean;
8788
wrapperRef: RefObject<View>;
89+
wrapperOffset: number;
90+
setWrapperOffset: React.Dispatch<React.SetStateAction<number>>;
91+
elements: Elements;
92+
setElements: React.Dispatch<React.SetStateAction<Elements>>;
93+
inputs: InputType;
94+
setInputs: React.Dispatch<React.SetStateAction<InputType>>;
95+
currentFocus?: null | Elements[0];
8896
} | null>(null);
8997

9098
const SmartScrollProvider = ({ children }: { children: React.ReactNode }) => {
9199
const scrollRef = useAnimatedRef<Animated.ScrollView>();
92100
const scrollY = useSharedValue(0);
93101
const wrapperRef = React.useRef<View>(null);
94-
const [isReady, setIsReady] = useState(false);
95-
const currentFocus = useAtomValue(currentFocusAtom);
102+
const [isReady, setIsReady] = useState(true);
103+
const [elements, setElements] = useState<Elements>({});
104+
const [wrapperOffset, setWrapperOffset] = useState(0);
105+
const [inputs, setInputs] = useState<InputType>({});
106+
107+
const currentFocus = useMemo(
108+
() =>
109+
Object.keys(elements)
110+
.map((key) => elements[key])
111+
.find((el) => el?.isFocus),
112+
[elements]
113+
);
96114

97115
// we have a flick on first focus so we make the scrollview wait a bit before animate
98116
useLayoutEffect(() => {
@@ -103,7 +121,19 @@ const SmartScrollProvider = ({ children }: { children: React.ReactNode }) => {
103121

104122
return (
105123
<SmartScrollContext.Provider
106-
value={{ scrollRef, scrollY, isReady, wrapperRef }}
124+
value={{
125+
scrollRef,
126+
scrollY,
127+
isReady,
128+
wrapperRef,
129+
wrapperOffset,
130+
setWrapperOffset,
131+
elements,
132+
setElements,
133+
currentFocus,
134+
inputs,
135+
setInputs,
136+
}}
107137
>
108138
{children}
109139
</SmartScrollContext.Provider>
@@ -114,7 +144,9 @@ export const useSmartScrollContext = () => {
114144
const context = React.useContext(SmartScrollContext);
115145

116146
if (!context) {
117-
throw new Error('Plz wrap with SmartScrollProvider');
147+
throw new Error(
148+
'Component must be wrapped in a SmartScrollProvider. Please ensure the provider is included.'
149+
);
118150
}
119151

120152
return context;
@@ -154,29 +186,25 @@ export const ScrollView = (
154186
);
155187
};
156188

157-
const currentFocusAtom = selectAtom(elementsAtom, (val) =>
158-
Object.keys(val)
159-
.map((key) => val[key])
160-
.find((el) => el?.isFocus)
161-
);
162-
163189
export function useFormSmartScroll({
164190
padding = 0,
165191
}: {
166192
padding?: number;
167193
} = {}) {
168194
const insets = useSafeAreaInsets();
169-
const wrapperOffset = useAtomValue(wrapperOffsetAtom);
170-
171-
const { isReady, scrollY, scrollRef } = useSmartScrollContext();
195+
const {
196+
currentFocus,
197+
setElements,
198+
isReady,
199+
scrollY,
200+
scrollRef,
201+
wrapperOffset,
202+
inputs,
203+
setInputs,
204+
} = useSmartScrollContext();
172205

173206
const _keyboard = useKeyboard();
174207

175-
const setState = useSetAtom(elementsAtom);
176-
const [inputs, setInputs] = useAtom(inputsAtom);
177-
178-
const currentFocus = useAtomValue(currentFocusAtom);
179-
180208
const scrollHandler = useAnimatedScrollHandler((event) => {
181209
scrollY.value = event.contentOffset.y;
182210
});
@@ -247,6 +275,8 @@ export function useFormSmartScroll({
247275
}
248276
);
249277

278+
console.log({ isReady });
279+
250280
const translateStyle = useAnimatedStyle(() => {
251281
return {
252282
transform: [{ translateY: isReady ? translateY.value : 0 }],
@@ -278,7 +308,7 @@ export function useFormSmartScroll({
278308

279309
const onFocus = useCallback(
280310
(name: string) => () => {
281-
setState((s) => ({
311+
setElements((s) => ({
282312
...s,
283313
[name]: {
284314
height: 0,
@@ -289,12 +319,12 @@ export function useFormSmartScroll({
289319
},
290320
}));
291321
},
292-
[setState]
322+
[setElements]
293323
);
294324

295325
const onBlur = useCallback(
296326
(name: string) => () => {
297-
setState((s) => ({
327+
setElements((s) => ({
298328
...s,
299329
[name]: {
300330
height: 0,
@@ -305,7 +335,7 @@ export function useFormSmartScroll({
305335
},
306336
}));
307337
},
308-
[setState]
338+
[setElements]
309339
);
310340

311341
/**

src/ViewWrapper.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import type { PropsWithChildren } from 'react';
22
import React, { useCallback, useRef } from 'react';
33
import { View } from 'react-native';
44
import type { LayoutChangeEvent, ViewStyle } from 'react-native';
5-
import { useAtom } from 'jotai';
6-
import { elementsAtom } from './state';
75
import { useSmartScrollContext } from './Provider';
86

97
type Props = PropsWithChildren<{
@@ -12,17 +10,16 @@ type Props = PropsWithChildren<{
1210
}>;
1311

1412
const ViewWrapper = ({ name, style, children }: Props) => {
15-
const [state, setState] = useAtom(elementsAtom);
16-
const { wrapperRef } = useSmartScrollContext();
13+
const { wrapperRef, elements, setElements } = useSmartScrollContext();
1714

1815
const ref = useRef<View>(null);
1916

2017
const onLayout = useCallback(
2118
({ nativeEvent }: LayoutChangeEvent) => {
22-
const element = state[name];
23-
if (wrapperRef.current && !state[name]) {
19+
const element = elements[name];
20+
if (wrapperRef.current && !elements[name]) {
2421
ref.current?.measureLayout(wrapperRef.current, (_, y, _w, h) => {
25-
setState((s) => ({
22+
setElements((s) => ({
2623
...s,
2724
[name]: {
2825
...s[name],
@@ -34,16 +31,16 @@ const ViewWrapper = ({ name, style, children }: Props) => {
3431
}));
3532
});
3633
} else if (element) {
37-
setState({
38-
...state,
34+
setElements((s) => ({
35+
...s,
3936
[name]: {
4037
...element,
4138
height: nativeEvent.layout.height,
4239
},
43-
});
40+
}));
4441
}
4542
},
46-
[state, name, wrapperRef]
43+
[name, wrapperRef, elements]
4744
);
4845

4946
return (

src/state.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/withSmartScroll.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import React from 'react';
22
import SmartScrollView from './Provider';
3-
import { Provider } from 'jotai';
43

54
export const withSmartScroll = (child: (props?: any) => React.ReactElement) => {
6-
return (props?: any) => (
7-
<Provider>
8-
<SmartScrollView>{child(props)}</SmartScrollView>
9-
</Provider>
10-
);
5+
return (props?: any) => <SmartScrollView>{child(props)}</SmartScrollView>;
116
};

yarn.lock

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ __metadata:
7676
eslint-config-prettier: ^9.0.0
7777
eslint-plugin-prettier: ^5.0.1
7878
jest: ^29.7.0
79-
jotai: ^2.12.2
8079
prettier: ^3.0.3
8180
react: 18.3.1
8281
react-native: 0.76.7
@@ -11417,21 +11416,6 @@ __metadata:
1141711416
languageName: node
1141811417
linkType: hard
1141911418

11420-
"jotai@npm:^2.12.2":
11421-
version: 2.12.2
11422-
resolution: "jotai@npm:2.12.2"
11423-
peerDependencies:
11424-
"@types/react": ">=17.0.0"
11425-
react: ">=17.0.0"
11426-
peerDependenciesMeta:
11427-
"@types/react":
11428-
optional: true
11429-
react:
11430-
optional: true
11431-
checksum: 8af476c442a09be12fa5b09e2d20e47905575123a98ccb16981ee5f280af1da23a23095ee4f33fde799aed568ecee8971a39f9ed29f9ec4687f2e54180b97191
11432-
languageName: node
11433-
linkType: hard
11434-
1143511419
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
1143611420
version: 4.0.0
1143711421
resolution: "js-tokens@npm:4.0.0"

0 commit comments

Comments
 (0)