Skip to content

Commit

Permalink
fix: 修复Toast和Passcode组件的bug
Browse files Browse the repository at this point in the history
  • Loading branch information
chj-damon committed Jan 15, 2025
1 parent ea4ef3b commit c7c10df
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-eyes-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@td-design/react-native': patch
---

修复Toast和Passcode组件的bug
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ function NormalPicker<T>(props: ImperativeModalChildrenProps<NormalPickerProps<T
const {
title,
data,
request,
value,
onChange,
cancelText = '取消',
Expand All @@ -22,18 +23,15 @@ function NormalPicker<T>(props: ImperativeModalChildrenProps<NormalPickerProps<T
...restProps
} = props;

console.log(value, 'value');

const initialValue = data.length > 0 ? data[0].value : undefined;

const { selectedValue, handleOk, handleChange, handleClose } = useNormalPicker({
const { selectedValue, handleOk, handleChange, handleClose, options } = useNormalPicker({
data,
request,
value,
initialValue,
onChange,
closeModal,
});

if (data.length === 0) return null;
if (options.length === 0) return null;

return (
<>
Expand Down Expand Up @@ -61,7 +59,7 @@ function NormalPicker<T>(props: ImperativeModalChildrenProps<NormalPickerProps<T
</Pressable>
</Flex>
<Box height={px(200)}>
<WheelPicker {...restProps} data={data} value={selectedValue} onChange={handleChange} />
<WheelPicker {...restProps} data={options} value={selectedValue} onChange={handleChange} />
</Box>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { NormalPickerProps } from '../../type';

export default function useNormalPicker<T>({
value,
initialValue,
onChange,
closeModal,
}: ImperativeModalChildrenProps<Omit<NormalPickerProps<T>, 'data'> & { initialValue?: T }>) {
}: ImperativeModalChildrenProps<Omit<NormalPickerProps<T>, 'data'>>) {
const initialValue = data.length > 0 ? data[0].value : undefined;

const [selectedValue, selectValue] = useSafeState<T | undefined>(value || initialValue);

const handleChange = (val: PickerData<T>) => {
Expand Down
10 changes: 9 additions & 1 deletion packages/react-native-picker/src/picker/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { CascadePickerItemProps, WheelPickerPropsBase } from '../components/Whee

export interface PickerProps<T> extends WheelPickerPropsBase {
/** 选择项列表 */
data: CascadePickerItemProps<T>[];
data?: CascadePickerItemProps<T>[];
/** 请求数据 */
request?: () => Promise<CascadePickerItemProps<T>[]>;
/** 是否级联 */
cascade?: boolean;
/** 展示几列 */
cols?: number;
value?: T[] | T;
onChange?: (value?: T extends (infer U)[] ? U[] : T) => void;
/** 字段名 */
fieldNames?: {
label?: string;
value?: string;
children?: string;
};
}

/** 弹窗Picker的属性 */
Expand Down
16 changes: 5 additions & 11 deletions packages/react-native/src/passcode/PasscodeItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, memo, RefObject, useEffect } from 'react';
import React, { forwardRef, memo } from 'react';
import { Platform, StyleSheet, TextInput } from 'react-native';

import { useTheme } from '@shopify/restyle';
Expand All @@ -18,9 +18,9 @@ const PasscodeItem = forwardRef<TextInput, PasscodeItemProps>(
inputContainerStyle,
focusStyle,
inputStyle,
handleTextChange,
inputValue,
handleKeyPress,
handleChangeText,
selectTextOnFocus,
...rest
},
Expand All @@ -29,13 +29,6 @@ const PasscodeItem = forwardRef<TextInput, PasscodeItemProps>(
const theme = useTheme<Theme>();
const [focused, { setTrue, setFalse }] = useBoolean(false);

useEffect(() => {
(ref as RefObject<TextInput>)?.current?.setNativeProps({
value: inputValue,
text: inputValue,
});
}, [inputValue]);

const styles = StyleSheet.create({
input: {
textAlign: 'center',
Expand All @@ -61,8 +54,8 @@ const PasscodeItem = forwardRef<TextInput, PasscodeItemProps>(
autoComplete="off"
onBlur={setFalse}
onFocus={setTrue}
onChangeText={handleTextChange}
onKeyPress={handleKeyPress}
onKeyPress={handleKeyPress} // 键盘按下事件, 只用来处理键盘按下删除键的场景
onChangeText={handleChangeText} // 输入框内容改变事件, 用来处理输入框内容改变的场景
selectionColor={theme.colors.primary200}
style={[styles.input, inputStyle]}
textContentType={isOTPSupported ? 'oneTimeCode' : 'none'}
Expand All @@ -73,6 +66,7 @@ const PasscodeItem = forwardRef<TextInput, PasscodeItemProps>(
ios: selectTextOnFocus,
android: true,
})}
value={inputValue}
{...rest}
/>
</Box>
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native/src/passcode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const Passcode = forwardRef<PasscodeRef, PasscodeProps>(
},
ref
) => {
const { otpCode, inputs, handleKeyPress, handleTextChange } = usePasscode({
const { otpCode, inputs, handleKeyPress, handleChangeText } = usePasscode({
onChange,
value,
count,
Expand Down Expand Up @@ -56,7 +56,7 @@ const Passcode = forwardRef<PasscodeRef, PasscodeProps>(
clearTextOnFocus={clearTextOnFocus}
focusStyle={focusStyle}
handleKeyPress={handleKeyPress(index)}
handleTextChange={handleTextChange(index)}
handleChangeText={handleChangeText(index)}
inputContainerStyle={inputContainerStyle}
inputStyle={inputStyle}
inputValue={inputValue}
Expand Down
1 change: 1 addition & 0 deletions packages/react-native/src/passcode/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default (state: ReducerState, { type, payload }: Actions) => {
return {
...state,
otpCode,
index: (payload as SetOtpTextForIndexPayload).index,
};
}

Expand Down
6 changes: 3 additions & 3 deletions packages/react-native/src/passcode/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface PasscodeProps extends Omit<TextInputProps, 'onChange' | 'onChan
/** 验证码长度 */
count?: number;
/** 验证码输入完成后的回调 */
onFinish?: () => void;
onFinish?: (value: string) => void;
}

export interface PasscodeRef {
Expand All @@ -45,10 +45,10 @@ export interface PasscodeItemProps extends TextInputProps {
focusStyle?: StyleProp<ViewStyle>;
/** TextInput的样式 */
inputStyle?: StyleProp<TextStyle>;
/** 文字改变时的回调事件 */
handleTextChange: (text: string) => void;
/** 输入的值 */
inputValue: string;
/** 按键按下的回调事件 */
handleKeyPress: (keyPressEvent: NativeSyntheticEvent<TextInputKeyPressEventData>) => void;
/** 输入框内容改变时的回调事件 */
handleChangeText: (text: string) => void;
}
69 changes: 27 additions & 42 deletions packages/react-native/src/passcode/usePasscode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ForwardedRef, RefObject, useEffect, useImperativeHandle, useReducer, useRef } from 'react';
import { Keyboard, NativeSyntheticEvent, Platform, TextInput, TextInputKeyPressEventData } from 'react-native';

import { useMemoizedFn, useSafeState } from '@td-design/rn-hooks';
import { useMemoizedFn } from '@td-design/rn-hooks';

import { fillOtpCode } from './helpers';
import reducer from './reducer';
Expand All @@ -19,7 +19,6 @@ export default function usePasscode({
}) {
const previousCopiedText = useRef<string>('');
const inputs = useRef<Array<RefObject<TextInput>>>([]);
const [index, setIndex] = useSafeState(0);

const [{ otpCode, hasKeySupport }, dispatch] = useReducer(reducer, {
otpCode: fillOtpCode(count, value),
Expand All @@ -36,13 +35,6 @@ export default function usePasscode({
}
}, [value, count]);

const fillInputs = useMemoizedFn((code: string) => {
dispatch({
type: 'setOtpCode',
payload: { count, code },
});
});

useImperativeHandle(ref, () => ({
reset: () => {
dispatch({ type: 'clearOtp', payload: count });
Expand Down Expand Up @@ -75,65 +67,58 @@ export default function usePasscode({
text: '',
},
});
focusInput(inputIndex - 1);
};

const handleChangeText = (index: number) => (text: string) => {
handleInputTextChange(text, index);
};

const handleKeyPress =
(index: number) =>
({ nativeEvent: { key } }: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
const text = key === 'Backspace' || key.length > 1 ? '' : key;
handleInputTextChange(text, index);

if (Platform.OS === 'android' && !hasKeySupport && !isNaN(parseInt(key)))
if (Platform.OS === 'android' && !hasKeySupport && !isNaN(parseInt(key))) {
dispatch({ type: 'setHasKeySupport', payload: true });
}

if (key === 'Backspace') {
// 当前输入框的值
const value = otpCode[`${index}`];
// 清除当前输入框的值
handleClearInput(index);
// 如果当前输入框的值为空,则聚焦上一个输入框
if (!value) {
focusInput(index - 1);
}
}
};

const handleTextChange = (index: number) => (text: string) => {
if (
(Platform.OS === 'android' && !hasKeySupport) ||
// Pasted from input accessory
(Platform.OS === 'ios' && text.length > 1)
) {
handleInputTextChange(text, index);
}
};

const handleInputTextChange = (text: string, index: number): void => {
setIndex(index);

if (!text.length) {
handleClearInput(index);
}

if (text.length > 1) {
handleClearInput(index);
Keyboard.dismiss();
return fillInputs(text);
}

if (text) {
if ([1, 2, 3, 4, 5, 6, 7, 8, 9, 0].includes(parseInt(text))) {
dispatch({
type: 'setOtpTextForIndex',
payload: {
text,
index,
},
});
focusInput(index + 1);
} else {
handleClearInput(index);
}
focusInput(index + 1);
};

useEffect(() => {
if (index === count - 1) {
onFinish?.();
const value = Object.values(otpCode).join('');
if (value.length === count) {
onFinish?.(value);
Keyboard.dismiss();
}
}, [index, count]);
}, [count, otpCode]);

return {
otpCode,
inputs,
handleKeyPress: useMemoizedFn(handleKeyPress),
handleTextChange: useMemoizedFn(handleTextChange),
handleChangeText: useMemoizedFn(handleChangeText),
};
}
15 changes: 14 additions & 1 deletion packages/react-native/src/toast/useToast.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { BackHandler } from 'react-native';

import { useMemoizedFn } from '@td-design/rn-hooks';

Expand All @@ -18,8 +19,11 @@ export default function useToast() {
clearTimeout(timer.current);
};

/** 每次显示前,先隐藏之前的Toast */
const show = (params: ToastProps) => {
if (visible) return;
if (visible) {
hide();
}

setOptions(params);
setVisible(true);
Expand All @@ -39,6 +43,15 @@ export default function useToast() {
return () => clearTimeout(timer.current);
}, [visible, options]);

/** 当Toast显示的时候,不允许安卓物理返回键可用 */
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', () => visible);

return () => {
BackHandler.removeEventListener('hardwareBackPress', () => false);
};
}, [visible]);

return {
visible,
options,
Expand Down

0 comments on commit c7c10df

Please sign in to comment.