You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行一次GraphQL查询后搜索按钮无法点击的修复问询

问题排查与修复方案

我帮你梳理下这个按钮失效的核心问题,主要出在Formik的状态管理时机和表单重置的时机上,咱们一步步来解决:

问题根源分析

  1. 表单重置打断状态更新:你在handleSubmitForm里直接调用了resetForm(),这会让Formik的dirty状态立刻变为false(因为表单回到初始值),而按钮的禁用条件是!isValid || !dirty,所以即使你后续输入有效手机号,dirty状态的更新可能因为之前的重置操作出现异常,导致按钮要么一直禁用,要么显示可点击但无法触发提交。
  2. 异步提交未正确处理状态:useFormik的onSubmit是异步场景,但你没有调用setSubmitting(false)来结束提交状态,这可能导致isSubmitting一直为true,间接影响表单的交互状态。
  3. 组件拆分后的状态隔离:把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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:27:50