经过整整 6 个小时的磨练,我终于得到了我的逻辑挑战问题的最小可重现示例:
import "expo-dev-client";
import React, { useState } from "react";
import { Button, View } from "react-native";
import Animated, { useSharedValue } from "react-native-reanimated";
export default function App() {
const [a, setA] = useState(false);
const toggleScreen = () => setA((p) => !p);
if (a) return <Content toggleScreen={toggleScreen} />;
else
return (
<View style={{ flex: 1, justifyContent: "center" }}>
<Button title="next" onPress={toggleScreen} />
</View>
);
}
function Content({ toggleScreen }: { toggleScreen: () => void }) {
const [a, setA] = useState(0);
const sharedValue = useSharedValue(0);
return (
<View style={{ flexGrow: 1, paddingTop: 50 }}>
<View style={{ flexDirection: "row" }}>
<Button title="back" onPress={toggleScreen} />
<Button
title={`update state ${a}`}
onPress={() => setA((p) => p + 1)}
/>
</View>
<View style={{ flexGrow: 1, justifyContent: "center" }}>
<Animated.Text
style={{ position: "absolute", borderWidth: 1, top: sharedValue }}
>
{`using shared value (${sharedValue.value})`}
</Animated.Text>
<Animated.Text style={{ position: "absolute", top: 0 }}>
using absolute number
</Animated.Text>
</View>
</View>
);
}
当按下“更新状态”按钮时,使用共享值的
Animated.Text
组件会跳转到父组件的中心,就好像 top
属性被忽略一样。即使状态对象完全未使用,并且共享值也从未更改,也是如此。 borderWidth
属性仍然有效。这应该发生吗?我不希望这样的事情发生。难道我做错了什么?我怎样才能让它不发生?
当您在 React 中更新状态时,它会触发组件及其子组件的重新渲染。此重新渲染可能会影响 Animated.Text 组件的布局。
解决方案 使用 useAnimatedStyle:确保您使用 useAnimatedStyle 来定义依赖于共享值的样式。该钩子确保当共享值更改时正确地重新计算样式。
Memoize Components:使用 React.memo 来记住状态改变时不需要重新渲染的组件。这可以防止不必要的重新渲染和布局重新计算。
分离组件:将内容组件拆分为更小的组件,以将依赖于共享值的部分与依赖于状态的部分隔离。
import "expo-dev-client";
import React, { useState } from "react";
import { Button, View } from "react-native";
import Animated, { useSharedValue, useAnimatedStyle } from "react-native-reanimated";
export default function App() {
const [a, setA] = useState(false);
const toggleScreen = () => setA((p) => !p);
if (a) return <Content toggleScreen={toggleScreen} />;
else
return (
<View style={{ flex: 1, justifyContent: "center" }}>
<Button title="next" onPress={toggleScreen} />
</View>
);
}
const Content = React.memo(({ toggleScreen }) => {
const [a, setA] = useState(0);
const sharedValue = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
return {
top: sharedValue.value,
borderWidth: 1,
};
});
return (
<View style={{ flexGrow: 1, paddingTop: 50 }}>
<View style={{ flexDirection: "row" }}>
<Button title="back" onPress={toggleScreen} />
<Button
title={`update state ${a}`}
onPress={() => setA((p) => p + 1)}
/>
</View>
<View style={{ flexGrow: 1, justifyContent: "center" }}>
<Animated.Text style={[animatedStyle, { position: "absolute" }]}>
{`using shared value (${sharedValue.value})`}
</Animated.Text>
<Animated.Text style={{ position: "absolute", top: 0 }}>
using absolute number
</Animated.Text>
</View>
</View>
);
});
export default App;