React Native中Axios POST请求重复执行问题求助
React Native注册功能POST请求重复提交问题
点击提交按钮后,Axios的POST请求会执行两次,数据库中新增两条记录,第一条为空数据,第二条为填写的有效数据。相关代码如下:
import React, { useState } from "react"; import { useNavigation } from "@react-navigation/native"; import { A } from "@expo/html-elements"; import { LogoImage } from "../assets/"; import MaterialIcons from "react-native-vector-icons/MaterialIcons"; import Checkbox from "expo-checkbox"; import axios from "axios"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { View, Text, Image, ScrollView, Pressable } from "react-native"; import { NativeBaseProvider, Button, FormControl, Input, Select, } from "native-base"; const SignUp = (props) => { const navigation = useNavigation(); const [show, setShow] = useState(false); const [isCondition, SetIsCondition] = useState(false); const [isLoading, setIsLoading] = useState(false); const [inputData, setInputData] = useState({ userName: "", password: "", lastName: "", job: "", jobCategory: "", }); const [isDataEmpty, setIsDataEmpty] = useState({ userName: false, password: false, lastName: false, job: false, jobCategory: false, }); const submitData = () => { if ( inputData.userName != "" && inputData.password.length >= 8 && inputData.lastName != "" && inputData.job != "" && inputData.jobCategory != "" && isCondition ) { // api setIsLoading(true); axios({ method: "POST", url: "http://localhost/chantier237/API/AUTH/st_signup.php", responseType: "json", headers: { "Access-Control-Allow-Origin": "*" }, data: { userName: inputData.userName, password: inputData.password, lastName: inputData.lastName, job: inputData.job, jobCategory: inputData.jobCategory, }, }) .then((response) => { console.log(response.data); if (response.data.status == "ERROR") { //TOAST THE ERROR console.log(response.data.message); } else { // save the datauser in state and in local AsyncStorage.setItem( "st_chantier237_user", JSON.stringify({ userId: response.data.data.id, isAbonned: response.data.data.is_abon, dateAbonned: response.data.data.date_abon, }) ); props.setStateUser({ userId: response.data.data.id, isAbonned: response.data.data.is_abon, dateAbonned: response.data.data.date_abon, }); } // navigation.replace("Home"); }) .catch((error) => { // toat error console.log(error); }) .finally(() => { setIsLoading(false); }); } else { if (inputData.userName == "") { setIsDataEmpty((prev) => { return { ...prev, userName: true }; }); } else { setIsDataEmpty((prev) => { return { ...prev, userName: false }; }); } if (inputData.password.length < 8) { setIsDataEmpty((prev) => { return { ...prev, password: true }; }); } else { setIsDataEmpty((prev) => { return { ...prev, password: false }; }); } if (inputData.lastName == "") { setIsDataEmpty((prev) => { return { ...prev, lastName: true }; }); } else { setIsDataEmpty((prev) => { return { ...prev, lastName: false }; }); } if (inputData.job == "") { setIsDataEmpty((prev) => { return { ...prev, job: true }; }); } else { setIsDataEmpty((prev) => { return { ...prev, job: false }; }); } if (inputData.jobCategory == "") { setIsDataEmpty((prev) => { return { ...prev, jobCategory: true }; }); } else { setIsDataEmpty((prev) => { return { ...prev, jobCategory: false }; }); } if (!isCondition) { console.log("condition not"); } } }; return ( <NativeBaseProvider> <ScrollView className="bg-white flex-1"> <View className="items-center justify-center pt-7 w-full pb-10"> <View className="w-32 h-32 mb-2"> <Image source={LogoImage} className="w-full h-full object-cover" /> </View> <View className="flex-col items-center justify-center w-full"> <Text className="text-[30px] text-[black] mb-1 text-bold"> Inscription </Text> <Text>creer votre compte</Text> </View> <View className="w-full px-8 items-center justify-center flex-col pt-5 gap-5"> <FormControl isInvalid={isDataEmpty.userName}> <Input placeholder="Nom d'utilisateur" type="text" className="text-sm" name="userName" onChangeText={(value) => setInputData({ ...inputData, userName: value }) } /> <FormControl.HelperText> Veuillez renseigner un seul nom </FormControl.HelperText> <FormControl.ErrorMessage> Ce champ ne doit pas etre vide. </FormControl.ErrorMessage> </FormControl> <FormControl isInvalid={isDataEmpty.lastName}> <Input placeholder="Votre nom" type="text" className="text-sm" onChangeText={(value) => setInputData({ ...inputData, lastName: value }) } /> <FormControl.ErrorMessage> Ce champ ne doit pas etre vide. </FormControl.ErrorMessage> </FormControl> <FormControl isInvalid={isDataEmpty.password}> <Input placeholder="Mot de passe" type={show ? "text" : "password"} className="text-sm" onChangeText={(value) => setInputData({ ...inputData, password: value }) } InputRightElement={ <Pressable onPress={() => setShow(!show)}> <MaterialIcons name={show ? "visibility" : "visibility-off"} size={25} mr="2" color="black" style={{ marginRight: 13 }} /> </Pressable> } /> <FormControl.ErrorMessage> Le mot de passe doit contenir au moins 8 caracteres </FormControl.ErrorMessage> </FormControl> <FormControl isInvalid={isDataEmpty.job}> <Input placeholder="Nom de votre metier" type="text" className="text-sm" onChangeText={(value) => setInputData({ ...inputData, job: value }) } /> <FormControl.ErrorMessage> Ce champ ne doit pas etre vide. </FormControl.ErrorMessage> </FormControl> <FormControl isInvalid={isDataEmpty.jobCategory}> <Select accessibilityLabel="Selectionner votre categorie" placeholder="Selectionner votre categorie" className="text-sm" _selectedItem={{ bg: "teal.600", }} mt="1" onValueChange={(value) => setInputData({ ...inputData, jobCategory: value }) } > <Select.Item label="UX Research" value="ux" /> <Select.Item label="Web Development" value="web" /> <Select.Item label="Cross Platform Development" value="cross" /> <Select.Item label="UI Designing" value="ui" /> <Select.Item label="Backend Development" value="backend" /> </Select> <FormControl.ErrorMessage> Ce champ ne doit pas etre vide. </FormControl.ErrorMessage> </FormControl> <FormControl className="w-full px-7"> <View className="flex-row text-center items-center justify-start"> <Checkbox value={isCondition} onValueChange={SetIsCondition} color="blue" /> <Text className="text-center text-sm"> J'accepte les{" "} <A href="https://stcode-portfolio.netlify.app" style={{ color: "blue", textDecorationLine: "underline", }} > termes et conditions d'utilisation{" "} </A> et la{" "} <A href="https://stcode-portfolio.netlify.app" style={{ color: "blue", textDecorationLine: "underline", }} > politique de confidentialite </A> </Text> </View> </FormControl> <Button style={{ backgroundColor: "black", width: "100%", height: 60, borderRadius: 7, fontSize: 25, }} onPress={submitData} size="lg" colorScheme="black" isLoading={isLoading} isLoadingText="Inscription" > S'inscrire </Button> <Text className="items-center justify-center w-full text-center"> Vous avez deja un compte ?{" "} <Text className="text-[15px] text-blue-700" onPress={() => { navigation.navigate("Signin"); }} > Connectez-vous </Text> </Text> </View> </View> </ScrollView> </NativeBaseProvider> ); }; export default SignUp;
问题原因及解决方法
常见触发原因
- React Strict Mode 重复执行副作用:开发环境下,React Strict Mode会刻意重复执行副作用函数(如Axios请求),用于检测潜在代码问题,这是此类重复提交的高频诱因。
- 按钮事件重复绑定:组件重渲染可能导致
onPress事件被多次注册,或者用户误触按钮两次。 - 状态更新引发重渲染:
submitData中的状态更新(如setIsLoading、setIsDataEmpty)可能触发组件重渲染,进而导致请求逻辑再次执行。
具体修复方案
禁用React Strict Mode
找到项目入口文件(通常是App.js或index.js),移除包裹组件的<StrictMode>标签:// 原代码 <StrictMode> <NavigationContainer>...</NavigationContainer> </StrictMode> // 修改后 <NavigationContainer>...</NavigationContainer>添加请求锁阻止重复触发
在submitData函数开头增加判断,避免请求在未完成时再次执行:const submitData = () => { if (isLoading) return; // 新增此行,阻止重复触发 // 原有逻辑... }合并状态更新减少重渲染
将else块中多次调用的setIsDataEmpty合并为一次,减少不必要的组件重渲染:// 替换原else块中的多个setIsDataEmpty setIsDataEmpty({ userName: inputData.userName === "", password: inputData.password.length < 8, lastName: inputData.lastName === "", job: inputData.job === "", jobCategory: inputData.jobCategory === "" });检查组件是否重复挂载
确认SignUp组件是否在父组件中被多次渲染,比如是否嵌套在多个NativeBaseProvider或导航容器中,确保组件只被挂载一次。
内容的提问来源于stack exchange,提问作者Thierry SITIO
相关产品推荐
相关产品推荐

