React Native使用reducer时报“Invalid hook call”渲染错误崩溃如何解决
问题修复方案
核心错误原因
DeviceCard.tsx中调用了useNavigation钩子但未导入,直接触发Invalid hook call报错DeviceCard.tsx中所有展示文本未正确使用模板字符串反引号包裹,存在语法错误- 存在其他非直接报错的逻辑隐患,也建议同步修复
修复步骤
1. 修复DeviceCard.tsx
完整修正后的代码如下:
import { useEffect, useState } from "react"; import { StyleSheet, Text, TouchableOpacity } from "react-native"; import { Device } from "react-native-ble-plx"; import { StackNavigationProp } from '@react-navigation/stack'; // 新增导入useNavigation import { useNavigation } from '@react-navigation/native'; type DeviceCardProps = { device: Device; }; const DeviceCard = ({ device }: DeviceCardProps) => { console.log("I am here!"); const navigation = useNavigation<StackNavigationProp<any>>(); const [isConnected, setIsConnected] = useState(false); useEffect(() => { // 修正isConnected返回值的处理逻辑 device.isConnected().then((connected) => setIsConnected(connected)); }, [device]); return ( <TouchableOpacity style={styles.container} onPress={() => navigation.navigate('Device', { device })}> {/* 修正所有模板字符串的反引号包裹 */} <Text>{`Id : ${device.id}`}</Text> <Text>{`Name : ${device.name}`}</Text> <Text>{`Is connected : ${isConnected}`}</Text> <Text>{`RSSI : ${device.rssi}`}</Text> <Text>{`Manufacturer : ${device.manufacturerData?.replace(/[=]/g, '')}`}</Text> <Text>{`ServiceData : ${device.serviceData}`}</Text> <Text>{`UUIDS : ${device.serviceUUIDs}`}</Text> </TouchableOpacity> ); }; const styles = StyleSheet.create({ container: { backgroundColor: 'white', marginBottom: 12, borderRadius: 16, shadowColor: 'rgba(60,64,67,0.3)', shadowOpacity: 0.4, shadowRadius: 10, elevation: 4, padding: 12, }, }); export { DeviceCard };
2. 补充校验Navigation容器
确认你的App组件外层已经被@react-navigation/native的NavigationContainer组件包裹,useNavigation只能在Navigation容器的后代组件中调用,否则仍会触发hook调用错误。
3. (可选)优化App.tsx的头部组件
将ListHeaderComponent移到App组件外部,避免每次重渲染生成新的组件实例导致不必要的性能损耗:
// 移到App组件外部 const ListHeaderComponent = ({isLoading, dispatch, scanDevices}: {isLoading: boolean, dispatch: any, scanDevices: () => void}) => ( <View style={styles.body}> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Devices</Text> </View> <View style={styles.sectionContainer}> <Button title="Clear devices" onPress={() => dispatch({ type: 'CLEAR' })} /> {isLoading ? ( <View style={styles.activityIndicatorContainer}> <ActivityIndicator color={'teal'} size={25} /> </View> ) : ( <Button title="Scan devices" onPress={scanDevices} /> )} </View> </View> ); // App组件内调用时传入props即可 const App = () => { // ...原有逻辑 return ( <SafeAreaView style={styles.body}> <FlatList keyExtractor={(item) => item.id} data={scannedDevices} renderItem={({ item }) => <DeviceCard device={item} />} ListHeaderComponent={() => <ListHeaderComponent isLoading={isLoading} dispatch={dispatch} scanDevices={scanDevices} />} contentContainerStyle={styles.content} /> </SafeAreaView> ); }
4. (可选)恢复权限检查逻辑
将注释掉的权限检查逻辑放在useEffect中调用,不要直接在组件顶层执行异步函数:
const App = () => { useEffect(() => { const checkPermission = async () => { let granted = await requestLocationPermission(); while(!granted) { granted = await requestLocationPermission(); } } checkPermission(); }, []) // ...原有逻辑 }
内容的提问来源于stack exchange,提问作者Stefan
相关产品推荐
相关产品推荐

