执行一次GraphQL查询后搜索按钮无法点击的修复问询
问题排查与修复方案
我帮你梳理下这个按钮失效的核心问题,主要出在Formik的状态管理时机和表单重置的时机上,咱们一步步来解决:
问题根源分析
- 表单重置打断状态更新:你在
handleSubmitForm里直接调用了resetForm(),这会让Formik的dirty状态立刻变为false(因为表单回到初始值),而按钮的禁用条件是!isValid || !dirty,所以即使你后续输入有效手机号,dirty状态的更新可能因为之前的重置操作出现异常,导致按钮要么一直禁用,要么显示可点击但无法触发提交。 - 异步提交未正确处理状态:
useFormik的onSubmit是异步场景,但你没有调用setSubmitting(false)来结束提交状态,这可能导致isSubmitting一直为true,间接影响表单的交互状态。 - 组件拆分后的状态隔离:把
showUsers拆成User组件后,虽然隔离了渲染逻辑,但父组件的表单状态没有和子组件的操作(比如添加联系人后的重置)联动,导致表单状态没有及时更新。
具体修复步骤
1. 调整表单重置的时机
把resetForm()从handleSubmitForm里移到查询完成的回调getContactId中,确保查询完成后再重置表单,避免打断提交流程:
const getContactId = React.useCallback( (data: UsersLazyQueryHookResult) => { if (data) { if (data.users.nodes.length == 0) { Alert.alert('No User Found'); } else { setUserData(data); } } // 查询完成后再重置表单 resetForm(); }, [resetForm], );
2. 正确处理Formik的异步提交状态
在useFormik的onSubmit里,添加setSubmitting(false)来结束提交状态,避免表单一直处于提交中:
const { values, handleChange, handleSubmit, dirty, handleBlur, isValid, resetForm, isSubmitting, setSubmitting } = useFormik({ initialValues: { phoneNumber: '' }, validationSchema, onSubmit: async (values: FormValues) => { try { await handleSubmitForm(values); } finally { // 无论成功失败,都结束提交状态 setSubmitting(false); } }, });
3. 优化按钮的禁用条件
把isSubmitting也加入禁用条件,避免提交过程中重复点击,同时确保状态判断更准确:
<Button block success disabled={!isValid || !dirty || isSubmitting} onPress={handleSubmit} style={styles.button} > <Text>Speichern</Text> </Button>
4. 修复handleSubmitForm的逻辑
去掉里面的resetForm(),只负责触发查询:
const handleSubmitForm = React.useCallback( (values: FormValues) => { setIsSubmitted(true); const plusSign = '+'; const newPhoneNumber = plusSign.concat(values.phoneNumber); console.log('Submitted'); return loadUsers({ variables: { where: { phoneNumber: newPhoneNumber } }, }); }, [loadUsers], );
5. 联动子组件的操作与父组件状态
在User组件的添加联系人按钮点击后,通知父组件重置表单状态(比如把resetForm作为props传给User组件):
// 父组件传递resetForm给User <User data={userData} resetForm={resetForm} /> // User组件接收并使用 export const User: React.FunctionComponent<UserProps> = ({ data, resetForm }) => { // ...其他代码 <Button rounded style={styles.addButton} onPress={() => { addContact(Number(item.id)); resetForm(); // 添加联系人后重置表单 }} > <Icon name="plus" size={moderateScale(20)} color="black" /> </Button> }; // 更新UserProps类型 type UserProps = { data: UsersLazyQueryHookResult; resetForm: () => void; };
完整修复后的代码示例
import React, { useState, useCallback } from 'react'; import { View, Text, SafeAreaView, Alert } from 'react-native'; import { Button, Icon, Thumbnail, Item, Input } from 'native-base'; import { useFormik } from 'formik'; import { useUsersLazyQuery, useCreateUserRelationMutation } from './your-graphql-queries'; // 替换成你的实际导入 import { phoneNumberValidationSchema, FormValues, UsersLazyQueryHookResult, RelationType } from './your-types'; // 替换成你的实际导入 import { moderateScale } from './your-utils'; // 替换成你的实际导入 import { useNavigation } from '@react-navigation/native'; export const AddContactTry: React.FunctionComponent = () => { const validationSchema = phoneNumberValidationSchema; const navigation = useNavigation(); const [isSubmitted, setIsSubmitted] = useState(false); const [userData, setUserData] = useState<UsersLazyQueryHookResult | null>(null); const _onLoadUserError = useCallback((error: any) => { Alert.alert('Oops, try again later'); }, []); const { values, handleChange, handleSubmit, dirty, handleBlur, isValid, resetForm, isSubmitting, setSubmitting } = useFormik({ initialValues: { phoneNumber: '' }, validationSchema, onSubmit: async (values: FormValues) => { try { await handleSubmitForm(values); } finally { setSubmitting(false); } }, }); const getContactId = useCallback( (data: UsersLazyQueryHookResult) => { if (data) { if (data.users.nodes.length === 0) { Alert.alert('No User Found'); } else { setUserData(data); } } resetForm(); }, [resetForm], ); const [loadUsers] = useUsersLazyQuery({ onCompleted: getContactId, onError: _onLoadUserError, }); const handleSubmitForm = useCallback( (values: FormValues) => { setIsSubmitted(true); const plusSign = '+'; const newPhoneNumber = plusSign.concat(values.phoneNumber); console.log('Submitted'); return loadUsers({ variables: { where: { phoneNumber: newPhoneNumber } }, }); }, [loadUsers], ); return ( <SafeAreaView> <View style={styles.container}> <View style={styles.searchTopContainer}> <View> <View style={styles.searchFieldContainer}> <View style={styles.form}> <Item underline style={styles.newFieldInput}> <Icon name="mobile" color="black" size={26}></Icon> <Input onChangeText={handleChange('phoneNumber') as (text: string) => void} onBlur={handleBlur('phoneNumber') as (event: any) => void} value={values.phoneNumber} placeholder="49152901820" /> </Item> </View> <View style={styles.buttonContainer}> <Button block danger bordered style={styles.button} onPress={resetForm} > <Text>Abbrechen</Text> </Button> <Button block success disabled={!isValid || !dirty || isSubmitting} onPress={handleSubmit} style={styles.button} > <Text>Speichern</Text> </Button> </View> </View> </View> <User data={userData} resetForm={resetForm} /> </View> </View> </SafeAreaView> ); }; type UserProps = { data: UsersLazyQueryHookResult | null; resetForm: () => void; }; export const User: React.FunctionComponent<UserProps> = ({ data, resetForm }) => { const [ createUserRelationMutation, { loading: addingContactLoading, error: addingContactError, called: isMutationCalled }, ] = useCreateUserRelationMutation({ onCompleted: () => { Alert.alert('Contact Added'); }, }); const addContact = useCallback( (id: number) => { console.log('Whats the Id', id); createUserRelationMutation({ variables: { input: { relatedUserId: id, type: RelationType.Contact, userId: 30 }, }, }); }, [createUserRelationMutation], ); if (!addingContactLoading && isMutationCalled) { if (addingContactError) { Alert.alert('Unable to Add Contact'); } } if (!data) return null; return ( <View style={styles.users}> {data.users.nodes.map( (item: { firstName: string; lastName: string; id: number }) => { const userName = `${item.firstName} ${item.lastName}`; return ( <View style={styles.item} key={item.id}> <Thumbnail style={styles.thumbnail} source={{ uri: 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/afro_woman_female_person-512.png' }} ></Thumbnail> <Text style={styles.userName}>{userName}</Text> <View style={styles.addButtonContainer}> <Button rounded style={styles.addButton} onPress={() => { addContact(item.id); resetForm(); }} > <Icon name="plus" size={moderateScale(20)} color="black" /> </Button> </View> </View> ); }, )} </View> ); }; // 保留你自己的样式定义,这里是示例样式 const styles = { container: { flex: 1, padding: 16 }, searchTopContainer: { gap: 16 }, searchFieldContainer: { gap: 8 }, form: { gap: 8 }, newFieldInput: { marginVertical: 8 }, buttonContainer: { flexDirection: 'row', gap: 8, justifyContent: 'space-between' }, button: { flex: 1 }, users: { marginTop: 16, gap: 12 }, item: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 8, borderRadius: 8, backgroundColor: '#f0f0f0' }, thumbnail: { width: 48, height: 48, borderRadius: 24 }, userName: { flex: 1, fontSize: 16, fontWeight: '500' }, addButtonContainer: {} };
额外注意点
- 确保你的
phoneNumberValidationSchema逻辑正确,比如允许带加号或者不带,避免重置后输入的手机号无法通过验证。 - 检查
UsersLazyQueryHookResult的类型定义,确保data.users.nodes的类型是正确的,避免渲染时出现类型错误。
内容的提问来源于stack exchange,提问作者user12541823
相关产品推荐
相关产品推荐

