React Native中如何通过首个Picker值调用API填充第二个Picker
问题分析与解决方案
核心问题总结
你的第二个Picker无数据返回,主要是以下几个代码错误导致的:
- 箭头函数语法错误:
getBanksByCountry中解析响应的代码写错,导致无法正确获取银行数据 - 未触发银行API调用:选中国家时没有调用
getBanksByCountry函数 - useEffect无限循环:缺少依赖数组,导致重复调用获取国家的API
- 状态赋值错误:在获取国家列表时错误地给单个状态赋值数组属性
- 函数组件误用类组件语法:TextInput中使用
this.setState,不符合函数组件规范
修正后的完整代码
import { ImageBackground, Modal, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, Platform } from 'react-native'; import {Picker} from '@react-native-picker/picker'; import React, {useEffect, useState} from 'react'; import {useNavigation} from '@react-navigation/native'; import BackgroundOpacity from './BackgroundOpacity'; const InternationalPayments = () => { const navigation = useNavigation(); const [banks, setBanks] = useState([]); const [countries, setCountries] = useState([]); const [selectedCountry, setSelectedCountry] = useState(); const [selectedBank, setSelectedBank] = useState(); const [modalVisible, setModalVisible] = useState(false); // 为TextInput定义对应的状态 const [creditAccount, setCreditAccount] = useState(''); const [amount, setAmount] = useState(''); const [narration, setNarration] = useState(''); const [branchCode, setBranchCode] = useState(''); const [beneficiaryName, setBeneficiaryName] = useState(''); const getBanksByCountry = (symbol) =>{ // 修复箭头函数语法错误 fetch(`https://api.flutterwave.com/v3/banks/${symbol}`,{ method:'GET', headers:{ 'Content-type': 'application/json', 'Authorization': 'Bearer FLWSECK_TEST-72fe360edef17334f4817a17407011bb-X', }, }).then(response => response.json()) .then(responseJson =>{ setBanks(responseJson.data); // 去掉多余的单个银行状态赋值,因为banks是数组,单个状态无意义 }) .catch(error => { // 添加错误捕获,方便调试 console.error('获取银行数据失败:', error); }) } const getAllCountries = async () =>{ fetch('https://webserver-migospay.onrender.com/api/location/get-country',{ method:'GET', headers:{ 'Content-type': 'application/json' }, }).then(response => response.json()) .then(responseJson=>{ setCountries(responseJson.data); // 去掉错误的单个国家状态赋值,因为返回的是数组 }) .catch(error => { console.error('获取国家数据失败:', error); }) } // 添加依赖数组,只在组件挂载时调用一次 useEffect(()=>{ getAllCountries(); }, []) // 监听selectedCountry变化,调用银行API useEffect(()=>{ if(selectedCountry){ getBanksByCountry(selectedCountry); } }, [selectedCountry]) return ( <View style={styles.container}> <BackgroundOpacity display={Platform.OS === 'ios' ? false : modalVisible} /> <View style={styles.space} /> <ScrollView contentContainerStyle={{ justifyContent: 'space-between', alignItems: 'center', }}> <ImageBackground source={{ uri: "asset:/logo/bg.JPG", }} imageStyle={{borderRadius: 6}} style={{ top: -30, paddingTop: 95, alignSelf: "center", width: 328, height: 115, borderRadius: 9, justifyContent: "center", alignItems: "center", }}> <View> <Text style={styles.accText}>Wallet Balance</Text> <Text style={styles.text}> 250,000 </Text> </View> </ImageBackground> <View style={{ borderRadius: 5, borderWidth: 1, overflow: 'hidden', height: 53, padding: 0, borderColor: '#00BB23', }}> <Picker style={{ width: 300, height: 55, borderBottomWidth: 1, }} itemStyle={{ fontSize: 25, fontFamily: 'Poppins-Medium', }} selectedValue={selectedCountry} onValueChange={(value) => setSelectedCountry(value)} > <Picker.Item label="Select Country" value="" /> {countries.map((country, index) => ( <Picker.Item label={country.country_name} value={country.symbol} key={index} /> ))} </Picker> </View> <View style={styles.space}/> <View style={{ borderRadius: 5, borderWidth: 1, overflow: 'hidden', height: 53, padding: 0, borderColor: '#00BB23', }}> <Picker style={{ width: 300, height: 55, borderBottomWidth: 1, }} itemStyle={{ fontSize: 25, fontFamily: 'Poppins-Medium', }} selectedValue={selectedBank} onValueChange={(value) => setSelectedBank(value)} disabled={!selectedCountry} // 未选国家时禁用银行Picker > <Picker.Item label="Select Bank" value="" /> {banks.map((bank, index) => ( <Picker.Item label={bank.name} value={bank.code} key={index} /> ))} </Picker> </View> <View style={styles.space2}/> <TextInput placeholder="Destination Account" onChangeText={setCreditAccount} value={creditAccount} style={styles.input} /> <TextInput placeholder=" Amount" onChangeText={setAmount} value={amount} style={styles.input} /> <TextInput placeholder=" Narration" onChangeText={setNarration} value={narration} style={styles.input} /> <TextInput placeholder=" Destination Branch Code" onChangeText={setBranchCode} value={branchCode} style={styles.input} /> <TextInput placeholder=" Beneficiary Name" onChangeText={setBeneficiaryName} value={beneficiaryName} style={styles.input} /> <View style={{ borderRadius: 5, borderWidth: 1, overflow: 'hidden', height: 35, padding: 0, top: 10, borderColor: '#00BB23', }}> <Picker style={{ width: 300, height: 53, borderBottomWidth: 1, }} itemStyle={{ fontSize: 25, fontFamily: 'Poppins-Medium', }}> <Picker.Item label="Currency" value="accNum" /> <Picker.Item label="NGN" value="NGN" /> </Picker> </View> <TouchableOpacity onPress={() => { setModalVisible(true); }} style={styles.button}> <Text style={styles.loginbtn}> Transfer </Text> </TouchableOpacity> <Modal hasBackdrop={true} backdropOpacity={0.2} backdropColor="black" transparent visible={modalVisible} onRequestClose={() => setModalVisible(false)}> <View style={styles.modal}> <Text>Hello From Modal</Text> <TouchableOpacity onPress={() => setModalVisible(false)}> <Text>Close Modal</Text> </TouchableOpacity> </View> </Modal> </ScrollView> </View> ); }; export default InternationalPayments; const styles = StyleSheet.create({ container: { paddingTop: 40, flex: 1, flexDirection: 'row', justifyContent: 'space-between', }, modal: { top: '50%', height: '50%', backgroundColor: '#fff', }, accText: { top: -50, paddingTop: 10, justifyContent: 'center', alignItems: 'center', fontFamily: 'Poppins-Medium', fontSize: 12, color: 'white', textAlign: 'center', }, text: { top: -50, fontSize: 20, color: 'white', textAlign: 'center', fontFamily: 'Poppins-Bold', }, input: { top: 10, width: 300, height: 53, margin: 10, fontSize: 12, borderColor: '#00BB23', fontFamily: 'Poppins-Bold', borderRadius: 5, borderWidth: 1, marginBottom: 30, }, button: { marginTop: 40, width: 150, height: 50, padding: 10, borderRadius: 10, backgroundColor: '#00BB23', alignItems: 'center', }, Regbutton: { width: 150, height: 52, padding: 10, borderRadius: 10, backgroundColor: '#ffffff', alignItems: 'center', borderWidth: 2, borderColor: '#030303', }, loginbtn: { color: '#ffff', fontSize: 15, fontFamily: 'Poppins-Medium', }, AccountBalance: { fontSize: 13, fontWeight: 'bold', textAlign: 'left', }, loginbtn2: { color: '#030303', fontSize: 20, fontWeight: 'bold', }, logo: { width: 150, height: 150, }, space: { top: 10, width: 10, height: 20, }, space2: { width: 10, height: 10, }, imageStyle: { flexDirection: 'row', justifyContent: 'center', padding: 5, margin: 2, height: 15, width: 15, resizeMode: 'stretch', marginBottom: 8, marginTop: 8, alignItems: 'center', }, });
关键修正点说明
- 修复箭头函数语法:将
response = response.json()改为response => response.json(),确保正确解析API响应 - 添加国家选中监听:新增
useEffect监听selectedCountry变化,一旦选中国家就调用getBanksByCountry获取对应银行数据 - 修正useEffect依赖:给获取国家的
useEffect添加空依赖数组[],避免无限循环调用API - 移除错误状态赋值:删除
getAllCountries中对country_name和country_symbol的错误赋值,因为返回的是国家数组,无法直接取单个属性 - 替换TextInput的setState:为每个TextInput定义对应的useState状态,使用函数组件的方式处理输入值
- 优化用户体验:未选中国家时禁用银行Picker,避免无效操作
- 添加错误捕获:在fetch请求中添加
catch块,方便调试API调用失败的情况
内容的提问来源于stack exchange,提问作者Lucas
相关产品推荐
相关产品推荐

