Skip to content

Commit d53ca1e

Browse files
committed
refactor(hooks): extract classroom data hook
1 parent 346153b commit d53ca1e

8 files changed

Lines changed: 359 additions & 185 deletions

File tree

src/app/(mainPage)/classroom.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@ import { router, useFocusEffect } from 'expo-router';
22
import * as React from 'react';
33
import { StyleSheet, TouchableOpacity, View } from 'react-native';
44

5+
import { useClassroomData } from '@/hooks';
6+
57
import Modal from '@/components/modal';
68
import Text from '@/components/text';
79

8-
import { useHeaderRightStore } from '@/store/headerRight';
910
import { useClassroomWarningStore } from '@/store/classroom';
11+
import { useHeaderRightStore } from '@/store/headerRight';
1012
import useVisualScheme from '@/store/visualScheme';
1113

1214
import StarIcon from '@/assets/icons/star.svg';
13-
import {
14-
ClassroomContent,
15-
useClassroomData,
16-
} from '@/modules/mainPage/components/classroom';
15+
import { ClassroomContent } from '@/modules/mainPage/components/classroom';
1716

1817
export default function Classroom() {
1918
const currentStyle = useVisualScheme(state => state.currentStyle);

src/app/(mainPage)/classroomStar.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import * as React from 'react';
22
import { StyleSheet, View } from 'react-native';
33

4-
import {
5-
ClassroomContent,
6-
useClassroomData,
7-
} from '@/modules/mainPage/components/classroom';
4+
import { useClassroomData } from '@/hooks';
85

96
import { useHeaderRightStore } from '@/store/headerRight';
107
import useVisualScheme from '@/store/visualScheme';
118

9+
import { ClassroomContent } from '@/modules/mainPage/components/classroom';
10+
1211
export default function ClassroomStar() {
1312
const currentStyle = useVisualScheme(state => state.currentStyle);
1413
const setHeaderRight = useHeaderRightStore(state => state.setContent);

src/components/picker/index.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ const Picker: React.FC<DatePickerProps> = ({
7474
onConfirm,
7575
onClose,
7676
defaultValue,
77+
controlledValue,
78+
onColumnChange,
7779
prefixes,
7880
mode = 'bottom',
7981
style,
@@ -97,19 +99,39 @@ const Picker: React.FC<DatePickerProps> = ({
9799
: commonColors.darkGray;
98100
}, [themeName]);
99101
const [pickerValue, setPickerValue] = useState<(string | number)[]>([]);
102+
// 用 ref 追踪上一次值,用于检测哪列发生了变化
103+
const prevPickerValue = React.useRef<(string | number)[]>([]);
104+
100105
const title = useMemo(
101106
() => titleDisplayLogic(pickerValue, data),
102107
[pickerValue, data]
103108
);
104109
const isBottomMode = useMemo(() => {
105110
return mode !== 'middle';
106111
}, [mode]);
107-
// 默认选择逻辑
112+
// 默认选择逻辑(仅 mount 时)
108113
useEffect(() => {
109-
handlePick(defaultValue ? defaultValue : data.map(item => item[0].value));
114+
const initial = defaultValue ?? data.map(item => item[0].value);
115+
prevPickerValue.current = initial;
116+
setPickerValue(initial);
110117
}, []);
118+
// 外部 controlledValue 变化时同步内部状态(用于级联重置)
119+
useEffect(() => {
120+
if (!controlledValue) return;
121+
const serialized = JSON.stringify(controlledValue);
122+
if (serialized === JSON.stringify(prevPickerValue.current)) return;
123+
prevPickerValue.current = [...controlledValue];
124+
setPickerValue([...controlledValue]);
125+
}, [JSON.stringify(controlledValue)]);
126+
111127
const handlePick = (pickedValue: (string | number)[]) => {
128+
const prev = prevPickerValue.current;
129+
const changedIndex = pickedValue.findIndex((v, i) => v !== prev[i]);
130+
prevPickerValue.current = [...pickedValue];
112131
setPickerValue(pickedValue);
132+
if (onColumnChange && changedIndex >= 0) {
133+
onColumnChange(pickedValue, changedIndex);
134+
}
113135
};
114136
const handleConfirm = () => {
115137
if (onConfirm) onConfirm(pickerValue.map(item => String(item)));

src/components/picker/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ export type ConnectorDataType = { content: string; columnIndex: number }[];
77
export interface DatePickerProps extends Omit<ModalTriggerProps, 'onConfirm'> {
88
/** 数据,必须有 label 和 value */
99
data?: PickerDataType;
10-
/** 默认选择的数据 */
10+
/** 默认选择的数据(仅首次 mount 时生效) */
1111
defaultValue?: (string | number)[];
12+
/** 外部控制当前选中值,变化时同步到内部 pickerValue */
13+
controlledValue?: (string | number)[];
1214
/** 与 Modal 不同,这里 onConfirm 需要带回选中数据 */
1315
onConfirm?: (_values: string[]) => void;
16+
/** 用户滑动任意列时实时回调,可用于级联更新 data */
17+
onColumnChange?: (
18+
_values: (string | number)[],
19+
_changedIndex: number
20+
) => void;
1421
/** 自定义标题展示逻辑 */
1522
titleDisplayLogic?: (
1623
_picked: (string | number)[],

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export { default as useBadgeSync } from './useBadgeSync';
2+
export { default as useClassroomData } from './useClassroomData';
23
export { default as useDebounce } from './useDebounce';
34
export { default as useJPush } from './useJPush';
45
export { default as useKeyboardStatus } from './useKeyboardStatus';

0 commit comments

Comments
 (0)