React Native + TypeScript登录功能中useReducer的TypeScript报错求助
Hey there! I see you're stuck with TypeScript errors in your useReducer implementation for the login input component—let's work through this together. The main issue here is overusing any; TypeScript can't properly validate your code when everything is typed as any, which leads to those confusing errors. Let's refactor your code with proper type definitions to fix this.
Step 1: Define Clear Types for State and Actions
First, let's create explicit types for your reducer's state and actions instead of relying on any:
// Shape of the input state managed by the reducer type InputState = { value: string; isValid: boolean; touched: boolean; }; // All possible actions the reducer can handle type InputAction = | { type: 'INPUT_CHANGE'; value: string; isValid: boolean } | { type: 'INPUT_BLUR' };
Step 2: Fix the Reducer Function
Update your reducer to use these defined types—no more any here:
const inputReducer = (state: InputState, action: InputAction): InputState => { switch(action.type) { case 'INPUT_CHANGE': return { ...state, value: action.value, isValid: action.isValid, }; case 'INPUT_BLUR': return { ...state, touched: true, }; default: return state; } };
Step 3: Correct the Component Props Type
Your original InputComponentProps was incorrectly structured—let's replace it with the actual props your component needs:
interface InputComponentProps { label: string; required?: boolean; email?: boolean; minLength?: number; errorText: string; id: string; onInputChange: (id: string, value: string, isValid: boolean) => void; // Catch-all for extra TextInput props you might spread (like placeholder, keyboardType) [key: string]: any; }
Step 4: Update the Component and useReducer Hook
Now adjust the component to use the new types, and fix the overly complex parameter type in handleChangeText (it's just a string!):
const InputComponent: React.FC<InputComponentProps> = (props) => { const [inputState, inputDispatch] = useReducer( inputReducer, { value: '', isValid: false, touched: false, } as InputState // Explicitly type the initial state ); const { onInputChange, id } = props; useEffect(() => { onInputChange(id, inputState.value, inputState.isValid); }, [onInputChange, id, inputState]); const handleChangeText = (text: string) => { const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; let isValid = true; if (props.required && text.trim().length === 0) isValid = false; if (props.email && !emailRegex.test(text.toLowerCase())) isValid = false; if (props.minLength && text.length < props.minLength) isValid = false; inputDispatch({ type: 'INPUT_CHANGE', value: text, isValid }); }; const handleBlur = () => inputDispatch({ type: 'INPUT_BLUR' }); return ( <View style={styles.formControl}> <Text style={styles.label}>{props.label}</Text> <TextInput {...props} style={styles.input} value={inputState.value} onChangeText={handleChangeText} onBlur={handleBlur} /> {!inputState.isValid && inputState.touched && ( <View> <Text style={styles.errorText}>{props.errorText}</Text> </View> )} </View> ); };
Key Fixes Explained
- Removed unnecessary
anytypes: By defining explicit types for state, actions, and props, TypeScript can now validate your code correctly and catch issues early. - Fixed the props interface: The original props had unrelated constants—we replaced it with the actual props your component uses.
- Simplified
handleChangeText: The input fromonChangeTextis always a string, so we set the parameter type tostringinstead of that overly complex (and incorrect) type you had. - Explicit initial state typing: We added
as InputStateto the initial state to ensure TypeScript recognizes its shape.
Full Refactored Code
Here's the complete, type-safe version of your component:
import React, { useEffect, useReducer } from "react"; import { StyleSheet, Text, TextInput, View } from "react-native"; type InputState = { value: string; isValid: boolean; touched: boolean; }; type InputAction = | { type: 'INPUT_CHANGE'; value: string; isValid: boolean } | { type: 'INPUT_BLUR' }; const inputReducer = (state: InputState, action: InputAction): InputState => { switch(action.type) { case 'INPUT_CHANGE': return { ...state, value: action.value, isValid: action.isValid, }; case 'INPUT_BLUR': return { ...state, touched: true, }; default: return state; } }; interface InputComponentProps { label: string; required?: boolean; email?: boolean; minLength?: number; errorText: string; id: string; onInputChange: (id: string, value: string, isValid: boolean) => void; [key: string]: any; } const InputComponent: React.FC<InputComponentProps> = (props) => { const [inputState, inputDispatch] = useReducer( inputReducer, { value: '', isValid: false, touched: false, } as InputState ); const { onInputChange, id } = props; useEffect(() => { onInputChange(id, inputState.value, inputState.isValid); }, [onInputChange, id, inputState]); const handleChangeText = (text: string) => { const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; let isValid = true; if (props.required && text.trim().length === 0) isValid = false; if (props.email && !emailRegex.test(text.toLowerCase())) isValid = false; if (props.minLength && text.length < props.minLength) isValid = false; inputDispatch({ type: 'INPUT_CHANGE', value: text, isValid }); }; const handleBlur = () => inputDispatch({ type: 'INPUT_BLUR' }); return ( <View style={styles.formControl}> <Text style={styles.label}>{props.label}</Text> <TextInput {...props} style={styles.input} value={inputState.value} onChangeText={handleChangeText} onBlur={handleBlur} /> {!inputState.isValid && inputState.touched && ( <View> <Text style={styles.errorText}>{props.errorText}</Text> </View> )} </View> ); }; const styles = StyleSheet.create({ formControl: { width: '100%', }, label: { fontFamily: 'OpenSansBold', marginVertical: 8, }, input: { paddingHorizontal: 2, paddingVertical: 5, borderBottomColor: '#ccc', borderBottomWidth: 1, }, errorText: { marginVertical: 5, color: '#cc7755' } }); export default InputComponent;
This should resolve those TypeScript errors and make your component type-safe. As you get more comfortable with TypeScript, try to avoid any whenever possible—clear types save you time in the long run by catching bugs early!
内容的提问来源于stack exchange,提问作者Joaquin Palacios

