下面是我的代码。我如何让我的药剂计数实时更新,同时避免"比以前的渲染更多的钩子"?错误。
我知道有一种方法可以做到这一点,但我很难理解它是如何工作的。如果有人能解释清楚,那就太好了,因为在我正在构建的东西中,我需要经常这样做。
import React, { useState } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import CustomButton from './CustomButton';
import { useFonts } from 'expo-font';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import player from './Player';
import usePotion from './UsePotion';
import { useNavigation } from '@react-navigation/native';
function addPotion() {
player.potions ++;
}
function BattleScreen() {
const [fontsLoaded, error] = useFonts({
'Valorax': require('./Fonts/Valorax.otf'),
});
const navigation = useNavigation()
const [potionMessage, setPotionMessage] = useState('');
if (!fontsLoaded) {
return <Text>Loading...</Text>;
}
return (
<View style={styles.container}>
<Text style={styles.text}>{player.potions}</Text>
{potionMessage && <Text style={styles.text}>{potionMessage}</Text>}
<View style={styles.topHalf}>
</View>
<View style={styles.bottomHalf}>
<View style={styles.buttonRow}>
<CustomButton onPress={() => handleAttack()} title='Attack'></CustomButton>
<CustomButton onPress={() => handleMagic()} title='Magic'></CustomButton>
</View>
<View style={styles.buttonRow}>
<CustomButton onPress={() => usePotion(setPotionMessage)} title='Use Potion'></CustomButton>
<CustomButton onPress={() => handleRun()} title='Run'></CustomButton>
<CustomButton onPress={() => navigation.navigate('Home')} title="Home"></CustomButton>
<CustomButton onPress={() => addPotion()} title="Add Potion"></CustomButton>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000000',
},
topHalf: {
flex: 1,
color: 'white',
},
bottomHalf: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap'
},
buttonRow: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-evenly',
flexWrap: 'wrap'
},
text: {
fontSize: 40,
color: 'white',
fontFamily: "Valorax",
}
});
export default BattleScreen;
我认为您看到的错误是由在JSX中调用usePotion
钩子引起的。钩子只能在其他钩子的顶层或者其他组件的顶层被调用。我不确定usePotion
与setPotionMessage
的关系,但它需要在组件的顶层调用。如果有一些事件你想从usePotion
触发,你需要从usePotion
返回它:
// usePotion
export default function usePotion(onChange){
// I assume usePotion would be structured like this
const [potion,setPotion] = useState({
hp:20,
message:''
})
// im assuming usePotion have some internal data
// that you would like to setPotionMessage to
const getPotionMessage = ()=>{
// because you past setPotionMessage I assume that you
// you want to subscribe to potion message changes?
onChange?.(potion.message)
return potion.message
}
return { potion,getPotionMessage}
}
现在你有一个钩子,返回一些关于药剂的状态,并允许你触发其他状态更新:
// top level of component
const {potion,getPotionMessage} = usePotion(setPotionMessage)
.
.
.
<CustomButton onPress={getPotionMessage} title='Use Potion'/>
最后,你需要让玩家进入反应生命周期。你可以将player。js转换成钩子,或者你可以你可以将player放入state:
// at the top level of the component
const [playerState,setPlayerState] = useState(player);
// maybe wrap addPotions in a useCallback?
const addPotion = ()=>{
setPlayerState(prev=>{
return {...prev,potions:prev.potions+1}
})
}
为了解决药水计数不更新的问题,简短的答案是你必须利用useState
钩子。简单地说,这是因为react只会在状态发生变化时进行渲染,而且它足够聪明,只会更新受状态变化影响的组件。
代码中的一个例子是,当调用setPotionMessage
方法并更新potionMessage
值时,唯一的更新反应是对JSX{potionMessage && <Text style={styles.text}>{potionMessage}</Text>}
。
这个数据需要保存在state中的原因如下:当react最初呈现时,它会编译一个包含所有返回组件的DOM树。这个DOM树看起来与您编写的JSX非常相似,具有额外的父标记和其他内容。当和仅当状态发生变化时,React将用新更改的数据编译一个新的DOM树,称为Virtual DOM
。然后,它将现有的DOM树与新的Virtual DOM树进行比较,以查找已更改的组件。最后,react会更新原来的DOM树,但是它只会更新那些已经改变的组件。
继续上面使用的potionMessage
示例。当BattleScreen
最初呈现时,虚拟DOM将像这样呈现JSX(从return
语句中提取):
<View>
<Text>3</Text>
<View></View>
...
</View>
但是,一旦调用setPotionMessage
并且potionMessage
值从''
更改为消息,react将重新编译Virtual DOM以确定更改了什么。它将重新编译成如下所示:
<View>
<Text>3</Text>
<View>
<Text>You really need a potion!</Text>
</View>
...
</View>
然后将原始DOM树与虚拟DOM树进行比较,找出不同之处。当它发现有什么不同时,它只呈现在实际DOM树上发生了变化的那部分树。
<View>
<Text>3</Text>
<View>
// ONLY this block is rerendered on the DOM
<Text>You really need a potion!</Text>
// ---
</View>
...
</View>
就需要的代码修改而言,PhantomSpooks概述了所需的更改。正如他们所提到的,让玩家进入反应生命周期是关键。