Adr*_*sso 5 javascript react-native firebase-realtime-database react-hooks
我有一个Host.js文件,其中包含一个曲目列表,该列表根据 setTrackListener() 函数的定义进行更新。我的问题是,每次调用该函数时,trackList 实际上并未更新。这是打印输出的示例:
Change detected in setTrackListener
LOG Track Name: TEST
LOG Current trackList:
LOG NewArray: TEST
LOG trackList after set:
Run Code Online (Sandbox Code Playgroud)
这是我的代码:
let authToken = "";
let roomID = "";
export default function Host({ navigation }) {
if (roomID == "") {
roomID = createRoom();
}
const [trackList, setTrackList] = useState([]);
if (authToken == "") {
getAuthAccessToken().then((t) => (authToken = t));
}
useEffect(() => {
setTrackListener(roomID, (t) => {
if (t != null) {
console.log("Track Name: " + t.name);
console.log("Current trackList: " + trackList);
const newArray = [...trackList];
newArray.push(t.name);
console.log("NewArray: " + newArray);
setTrackList(newArray);
console.log("trackList after set: " + trackList);
}
});
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>Hosting {roomID}</Text>
<FlatList
data={trackList}
renderItem={({ item }) => <Text style={styles.item}>{item}</Text>}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
title: {
color: "#590",
fontSize: 32,
},
});
Run Code Online (Sandbox Code Playgroud)
setTrackListener是在一个单独的文件中定义的,我在其中监听特定 Google Firebase 数据库的更改。这是该代码:
let setTrackListener = (id, onChange) => {
let tracksRef = ref(database, `rooms/${id}/tracks`);
onChildAdded(tracksRef, (snapshot) => {
const data = snapshot.val();
console.log("Change detected in setTrackListener");
onChange(data);
});
};
Run Code Online (Sandbox Code Playgroud)
如果我使用如下按钮更新 trackList,它会更新:
<Button
title={"Test"}
onPress={() => {
const newArray = [...trackList, "TEST"];
setTrackList(newArray);
console.log(trackList);
}}
></Button>
Run Code Online (Sandbox Code Playgroud)
TLDR:将函数setTrackList(newArray);内部转换useEffect为
setTrackList((trackList) => [...trackList, t.name]);
Run Code Online (Sandbox Code Playgroud)
我认为你对 setTrackListener 的实现是正确的。
所以我认为主要原因是trackListin 中的变量useEffect被包装在一个过时的闭包中,这是 Javascript 的一个特征,当你定义一个函数,并且它使用该函数外部的变量时,它将缓存该变量的值在定义函数时,并且始终返回相同的值(即本例中为空数组)。
要解决这个问题,您需要将trackList变量放入 的依赖数组中useEffect,或者将回调函数传递给 setState 函数以避免访问trackList内部useEffect。
建议:将setTrackList(newArray);useEffect 函数中的转换为
setTrackList((trackList) => [...trackList, t.name]);
Run Code Online (Sandbox Code Playgroud)
并且不要console.log(trackList)在回调函数中使用(你不会看到该值,因为它总是[]),或者将控制台日志放入函数体(相当脏,仅用于调试),或者在 的setTrackList回调中打印该值
setTrackList((trackList) => {
const newArray = [...trackList, t.name]
console.log(newArray)
return newArray
});
Run Code Online (Sandbox Code Playgroud)
如果这不起作用,您在输出中没有看到更新的 trackList 的另一个可能原因是React 不会立即更新状态,该setState函数将触发重新渲染,并且状态会在下一个批次中更新重新渲染。
但在许多情况下,更新会立即生效,但您不能依赖这一点。
PS 需要注意的一点是,卸载组件时useEffect并不会取消订阅onChildAdded,这可能会导致一些性能问题,因此最好重写setTrackListener和useEffect来
setTrackList((trackList) => {
const newArray = [...trackList, t.name]
console.log(newArray)
return newArray
});
Run Code Online (Sandbox Code Playgroud)
参考:
https://dmitripavlutin.com/react-hooks-stale-closures/#3-state-closure-in-useeffect
https://blog.logrocket.com/why-react-doesnt-update-state-immediately/
| 归档时间: |
|
| 查看次数: |
183 次 |
| 最近记录: |