React Native中onReserve执行后ModalView子组件无法实时更新的问题
React Native中执行onReserve后ModalView无法同步更新usermachine值
执行onReserve()方法后,ModalView组件里的usermachine值始终是更新前的旧值:
- 初始
usermachine为[],执行后变为[{"id":"1"}],但ModalView仍显示[] - 再次执行后
usermachine变为[{"id":"2"}],ModalView却显示[{"id":"1"}]
如何实现onReserve()执行后立即更新ModalView中显示的usermachine值?
ReserveScreen.js
import React, {useEffect, useState, useRef} from 'react'; import { SafeAreaView, ScrollView, StyleSheet, View, Alert, Button, Text, Modal, KeyboardAvoidingView, } from 'react-native'; import ModalView from '../components/ModalView.js'; import MachineView from '../components/MachineView.js'; import AsyncStorage from '@react-native-async-storage/async-storage'; const ReserveScreen = ({navigation, route, category}) => { const [machines, setMachines] = useState([]); const [change1, setChange1] = useState(0); function handlechange() { setChange1(change1 + 1); } const [loading, setLoading] = useState(0); const [loading2, setLoading2] = useState(0); const getmachineinfo = () => { setLoading2(loading2 + 1); fetch( 'https://so6wenvyg8.execute-api.ap-northeast-2.amazonaws.com/dev/machine', { method: 'GET', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }, ) .then(response => response.json()) .then(json => { let machinelist = []; for (let k = 0; k < json.machinearr.length; k++) { let id1 = -1; let name1 = ''; let category1 = -1; let waitnum1 = -1; for (let i = 0; i < json.machinearr[k].length - 1; i++) { switch (i) { case 0: id1 = Number(json.machinearr[k][i]); break; //machineid case 1: name1 = json.machinearr[k][i]; break; //machinename case 2: category1 = Number(json.machinearr[k][i]); break; //machinecategory case 3: continue; //machineimage case 4: waitnum1 = json.machinearr[k][i].length; break; //waitnumber } } let object = { name: name1, id: id1, waitnum: waitnum1, category: category1, }; machinelist.push(object); } setMachines(machinelist); setTimeout(() => { setLoading(loading+1); }, 1000); }) .catch(error => { console.error(error); return -1; }); }; useEffect(() => { console.log('useeffect'); getmachineinfo(); }, [change1]); const [username, setUsername] = useState(''); const getusername = async () => { try { const name = await AsyncStorage.getItem('@storage_username'); setUsername(name); return; } catch (e) { console.log(e); } }; getusername(); const [userid,setUserid]=useState(''); const getuserid= async()=>{ const value = await AsyncStorage.getItem('@storage_userid'); if (value !== null){ setUserid(value); } } getuserid(); const [myres, setMyres] = useState([]); const [loading3, setLoading3] = useState(0); const [loading4, setLoading4] = useState(0); const [usermachine, setUserMachine] = useState([]); const myReserve = () => { if (userid !== null) { const url = 'https://so6wenvyg8.execute-api.ap-northeast-2.amazonaws.com/dev/reservation?userid=' + userid; fetch(url, { method: 'GET', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }) .then(response => response.json()) .then(json => { Alert.alert(JSON.stringify(json)); setMyres(json["reservation"]); setUserMachine(myres); setLoading4(loading4 + 1); handlechange(); return myres; }) .catch(error => { console.error(error); }); } handlechange(); }; const [isreserved, setIsReserved] = useState(false); const onmyReserve = () => { setLoading3((loading3)=>{return loading3+1}); myReserve(); setTimeout(() => { if (myres.length > 0) { setIsReserved(true); } else { setIsReserved(false); } }, 2000); }; const [visible, setVisible] = useState(false); return ( <SafeAreaView style={{flex: 1, backgroundColor: 'white'}}> <Modal transparent={true} visible={visible} animationType="slide"> <View style={styles.modalContainer}> <View style={styles.modalContent}> {loading3!=loading4?(<View style={{flex: 7, justifyContent: 'center', alignItems: 'center'}}> <View style={{flex: 1}} /> <Text>현재 예약가능한 기구가 없습니다.</Text> <View style={{flex: 5}} /> </View>):(<ModalView isreserved={isreserved} usermachine={usermachine} handlerFunction={handlechange} change1={change1}></ModalView>)} <Button title={'확인'} onPress={() => { setVisible('false'); }}></Button> </View> </View> </Modal> <View style={styles.btn1view}> <Button title="나의 예약내역 조회/수정" color={'#26a96a'} onPress={() => { onmyReserve(); setVisible(true); }} /> </View> <View style={styles.seperator}></View> {loading != loading2 ? ( <View style={{flex: 7, justifyContent: 'center', alignItems: 'center'}}> <View style={{flex: 1}} /> <Text>현재 예약가능한 기구가 없습니다.</Text> <View style={{flex: 5}} /> </View> ) : ( <MachineView machine={machines} handlerFunction={handlechange} change1={change1}></MachineView> )} <View style={{height: '5%'}}></View> </SafeAreaView> ); }; const styles = StyleSheet.create({ seperator: { height: 1, backgroundColor: 'black', marginVertical: 10, }, btn1view: { flex: 0.6, backgroundColor: 'white', flexDirection: 'row-reverse', alignItems: 'center', }, categoryView: { flex: 0.5, backgroundColor: 'white', flexDirection: 'row', justifyContent: 'space-around', }, sortView: {flex: 0.5, flexDirection: 'row', marginLeft: 10}, scrollView: {flex: 6, backgroundColor: 'white'}, modalContainer: { flex: 1, padding: 20, justifyContent: 'center', }, modalContent: { flex: 0.5, backgroundColor: 'white', alignItems: 'center', justifyContent: 'center', borderColor: 'black', borderWidth: 4, borderRadius: 20, }, }); export default ReserveScreen;
ModalView.js
import React from 'react'; import {View, Text, Button, StyleSheet} from 'react-native'; import MachineModal from './MachineModal.js'; import Icon from 'react-native-vector-icons/MaterialIcons'; const ModalView = ({isreserved, usermachine, change1, handlerFunction}) => { const change2 = change1; function handleChange() { handlerFunction(); console.log('handlechange()'); } { return ( <View> <Text>나의 예약내역</Text> {usermachine.map(item => { return ( <View key={item.id}> <MachineModal name={item.name} id={item.id} waitnum={item.waitnum} change2={change2} handlerFunction={handleChange}></MachineModal> </View> ); })} </View> ); } }; const styles = StyleSheet.create({}); export default ModalView;
MachineModal.js
import React, {useState} from 'react'; import {View, Text, Button, Alert, StyleSheet, Image} from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; const MachineModal = ({name, id, waitnum, change2, handlerFunction}) => { const formatted = `기구명 : ${name}\n\n현재 대기인원 : ${waitnum}명`; const [userid, setUserid] = useState(''); const getusername = async () => { try { const uid = await AsyncStorage.getItem('@storage_userid'); setUserid(uid); return; } catch (e) { console.log(e); } }; getusername(); const change3 = change2; function handlechange1() { handlerFunction(); } const machinereturn = (machineid, usid) => { try { fetch( 'https://so6wenvyg8.execute-api.ap-northeast-2.amazonaws.com/dev/return', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ machineid: String(machineid), userid: String(usid), }), }, ) .then(response => response.json()) .then(json => { console.log(json); try { tim = json['time']; handlechange1(); Alert.alert('반납되었습니다.'); return 1; } catch (e) { handlechange1(); Alert.alert('반납에 실패했습니다.'); return -1; } }); } catch (e) { console.log('17'); console.log(e); } }; return ( <View style={styles.machine}> <Image source={require('../images/default_image.png')} style={{width: 70, height: 70}}></Image> <Text>{formatted}</Text> <Button title="예약취소" onPress={() => machinereturn(id, userid)}></Button> </View> ); }; const styles = StyleSheet.create({ machine: { flexDirection: 'row', justifyContent: 'space-around', paddingTop: 7, paddingBottom: 7, borderBottomColor: 'black', borderBottomWidth: 1, alignItems: 'center', }, }); export default MachineModal;
问题原因及修复方案
- 异步状态更新导致的旧值引用
在myReserve函数中,setMyres是异步操作,调用后立即用myres更新usermachine时,myres还是未更新的旧值,导致usermachine始终慢一步。
修复:
直接使用接口返回的最新数据更新usermachine:
.then(json => { Alert.alert(JSON.stringify(json)); const newReservations = json["reservation"]; setMyres(newReservations); setUserMachine(newReservations); // 直接用接口返回的新数据 setLoading4(prev => prev + 1); handlechange(); })
- 定时器依赖异步状态的不可靠性
onmyReserve中用setTimeout判断myres.length,但myres的更新是异步的,定时器触发时可能还未完成更新,导致isreserved状态错误。
修复:
在接口回调中直接判断并更新isreserved:
.then(json => { // ...其他代码 const newReservations = json["reservation"]; setIsReserved(newReservations.length > 0); })
同时删除onmyReserve中的setTimeout代码:
const onmyReserve = () => { setLoading3(prev => prev + 1); myReserve(); };
- 状态更新的闭包陷阱
多个状态更新(如setChange1、setLoading)直接依赖当前状态值,在异步场景下可能获取到旧值。
修复:
使用函数式更新获取最新状态:
function handlechange() { setChange1(prev => prev + 1); } // 所有类似的状态更新都改为函数式: setLoading2(prev => prev + 1); setLoading(prev => prev + 1); setLoading4(prev => prev + 1);
- Modal关闭按钮的类型错误
关闭Modal时传入了字符串'false',应该传入布尔值false:
<Button title={'확인'} onPress={() => { setVisible(false); }}></Button>
内容的提问来源于stack exchange,提问作者offtheglass
相关产品推荐
相关产品推荐

