状态不会在 React Native 上更新
State doesn't update on React Native
我正在做我的反应本机应用程序的通知页面。它有无限滚动和 "pull to refresh" 选项。进入它工作的页面,它也可以拉动刷新。
当我向下滚动时出现问题,因为它似乎调用服务器来获取新通知,但它没有连接到数组。
import React, { useState, useEffect, useCallback, Component } from "react";
import {
View,
Text,
FlatList,
Button,
Platform,
ActivityIndicator,
StyleSheet,
ScrollView,
RefreshControl,
SafeAreaView,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import i18n from "i18n-js";
import Colors from "../../constants/Colors";
import { getNotificationList } from "../../utils/NotificationsUtils";
import Card from "../../components/UI/Card";
const NotificationsScreen = (props) => {
const [refreshing, setRefreshing] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(0);
const [notifications, setNotifications] = useState([]);
const [error, setError] = useState();
const dispatch = useDispatch();
const onRefresh = useCallback(async () => {
setRefreshing(true);
setNotifications([]);
setPage(0);
console.log("-- Refreshing --");
getNotifications().then(() => {
setRefreshing(false);
});
}, [dispatch, setRefreshing]);
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
console.log(
"FETCH MORE from page " + newPage + " on array of " + notifications.length
);
getNotifications().then(() => {
setIsLoading(false);
});
}, [dispatch, getNotifications]);
const getNotifications = useCallback(async () => {
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
console.log(
"Setting " +
retrievedNotifications.response.notifications.length +
" new notifications on an already existing array of " +
notifications.length +
" elements"
);
let updatedNews = notifications.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
);
setNotifications(updatedNews);
} catch (err) {
setError(err.message);
}
setIsLoading(false);
}, [dispatch, setIsLoading, setNotifications, setError]);
useEffect(() => {
setIsLoading(true);
getNotifications(page).then(() => {
setIsLoading(false);
});
}, [dispatch, getNotifications]);
return (
<View>
{error ? (
<View style={styles.centered}>
<Text>Error</Text>
</View>
) : refreshing ? (
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
) : !notifications || !notifications.length ? (
<View style={styles.centered}>
<Text>No data found</Text>
</View>
) : (
<FlatList
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
data={notifications}
keyExtractor={(notification) => notification.notificationQueueId}
onEndReached={fetchMoreNotifications}
onEndReachedThreshold={0.5}
initialNumToRender={4}
renderItem={(itemData) => (
<View
style={{
marginTop: 10,
height: 150,
width: "100%",
}}
>
<Card style={{ height: 150, backgroundColor: "white" }}>
<Text style={{ fontSize: 16, color: Colors.black }}>
{itemData.item.text}
</Text>
</Card>
</View>
)}
/>
)}
</View>
);
};
const styles = StyleSheet.create({
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default NotificationsScreen;
如果我滚动到结束,它会触发 'fetchMoreNotifications' 函数,我会在控制台中看到:
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
...and so on
如您所见,即使我之前保存了通知,它也会显示 'existing array of 0 elements'。也许它对 useCallback 的依赖性有一些问题?
问题其实很简单。 getNotifications 函数是使用 useCallback
创建的,没有使用 notifications
作为依赖项。现在,当通知更新时,getNotications 函数仍然引用旧的通知值,因为关闭。
另请注意,您在设置页面状态后立即在 fetchMoreNotifications
上调用 getNotifications,但页面状态也受闭包约束,不会在同一个重新呈现中更新
这里的解决方法是使用函数的方式来setNotifications,在页面变化时使用useEffect来trigge4r getNotification
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
}, [dispatch, getNotifications]);
useEffect(() => {
setIsLoading(true);
getNotifications(page).then(() => {
setIsLoading(false);
});
}, [dispatch, page, getNotifications]);
const getNotifications = useCallback(async () => {
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
setNotifications(prevNotification => prevNotification.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
));
} catch (err) {
setError(err.message);
}
setIsLoading(false);
}, [dispatch, setIsLoading, setNotifications, setError]);
问题:
有两个主要问题,一个是 page
,第二个是 notifications
,由于 useCallback
和 dependencies
,useCallback 函数将始终指向旧值在更新的依赖项之一之前,它们不在依赖项中。
1)page
问题的解决方案:
将 newPage
作为参数传递给 getNotifications
,由于 setPage
的异步行为,它不会直接更新
第二次,要获取页面的更新值,您可以将 page
作为依赖项传递。
2)notification
问题的解决方案:
使用 setState(prevState => newState)
.
直接从其上一个状态值更新通知
解法:
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
console.log(
"FETCH MORE from page " + newPage + " on array of " + notifications.length
);
getNotifications(newPage).then(() => { // <---- Pass as param
setIsLoading(false);
});
}, [page]); // <---- Add page as dependency
const getNotifications = useCallback(
async page => { // <---- Get page as a param
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
setNotifications(prevNotification => prevNotification.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
)); // <---- Setting up state directly from previous value, instead of getting it from clone version of use callback
} catch (err) {
console.log(err);
setError(err.message);
}
setIsLoading(false);
},
[setIsLoading, setNotifications, setError]
);
工作演示:
检查控制台日志以获取更新的页面值,通知将在 Html 上自行呈现
NOTE : Removed some of your code just to improve code readability and
debug the issue
我正在做我的反应本机应用程序的通知页面。它有无限滚动和 "pull to refresh" 选项。进入它工作的页面,它也可以拉动刷新。 当我向下滚动时出现问题,因为它似乎调用服务器来获取新通知,但它没有连接到数组。
import React, { useState, useEffect, useCallback, Component } from "react";
import {
View,
Text,
FlatList,
Button,
Platform,
ActivityIndicator,
StyleSheet,
ScrollView,
RefreshControl,
SafeAreaView,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import i18n from "i18n-js";
import Colors from "../../constants/Colors";
import { getNotificationList } from "../../utils/NotificationsUtils";
import Card from "../../components/UI/Card";
const NotificationsScreen = (props) => {
const [refreshing, setRefreshing] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(0);
const [notifications, setNotifications] = useState([]);
const [error, setError] = useState();
const dispatch = useDispatch();
const onRefresh = useCallback(async () => {
setRefreshing(true);
setNotifications([]);
setPage(0);
console.log("-- Refreshing --");
getNotifications().then(() => {
setRefreshing(false);
});
}, [dispatch, setRefreshing]);
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
console.log(
"FETCH MORE from page " + newPage + " on array of " + notifications.length
);
getNotifications().then(() => {
setIsLoading(false);
});
}, [dispatch, getNotifications]);
const getNotifications = useCallback(async () => {
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
console.log(
"Setting " +
retrievedNotifications.response.notifications.length +
" new notifications on an already existing array of " +
notifications.length +
" elements"
);
let updatedNews = notifications.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
);
setNotifications(updatedNews);
} catch (err) {
setError(err.message);
}
setIsLoading(false);
}, [dispatch, setIsLoading, setNotifications, setError]);
useEffect(() => {
setIsLoading(true);
getNotifications(page).then(() => {
setIsLoading(false);
});
}, [dispatch, getNotifications]);
return (
<View>
{error ? (
<View style={styles.centered}>
<Text>Error</Text>
</View>
) : refreshing ? (
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
) : !notifications || !notifications.length ? (
<View style={styles.centered}>
<Text>No data found</Text>
</View>
) : (
<FlatList
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
data={notifications}
keyExtractor={(notification) => notification.notificationQueueId}
onEndReached={fetchMoreNotifications}
onEndReachedThreshold={0.5}
initialNumToRender={4}
renderItem={(itemData) => (
<View
style={{
marginTop: 10,
height: 150,
width: "100%",
}}
>
<Card style={{ height: 150, backgroundColor: "white" }}>
<Text style={{ fontSize: 16, color: Colors.black }}>
{itemData.item.text}
</Text>
</Card>
</View>
)}
/>
)}
</View>
);
};
const styles = StyleSheet.create({
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
export default NotificationsScreen;
如果我滚动到结束,它会触发 'fetchMoreNotifications' 函数,我会在控制台中看到:
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
...and so on
如您所见,即使我之前保存了通知,它也会显示 'existing array of 0 elements'。也许它对 useCallback 的依赖性有一些问题?
问题其实很简单。 getNotifications 函数是使用 useCallback
创建的,没有使用 notifications
作为依赖项。现在,当通知更新时,getNotications 函数仍然引用旧的通知值,因为关闭。
另请注意,您在设置页面状态后立即在 fetchMoreNotifications
上调用 getNotifications,但页面状态也受闭包约束,不会在同一个重新呈现中更新
这里的解决方法是使用函数的方式来setNotifications,在页面变化时使用useEffect来trigge4r getNotification
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
}, [dispatch, getNotifications]);
useEffect(() => {
setIsLoading(true);
getNotifications(page).then(() => {
setIsLoading(false);
});
}, [dispatch, page, getNotifications]);
const getNotifications = useCallback(async () => {
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
setNotifications(prevNotification => prevNotification.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
));
} catch (err) {
setError(err.message);
}
setIsLoading(false);
}, [dispatch, setIsLoading, setNotifications, setError]);
问题:
有两个主要问题,一个是 page
,第二个是 notifications
,由于 useCallback
和 dependencies
,useCallback 函数将始终指向旧值在更新的依赖项之一之前,它们不在依赖项中。
1)page
问题的解决方案:
将 newPage
作为参数传递给 getNotifications
,由于 setPage
的异步行为,它不会直接更新
第二次,要获取页面的更新值,您可以将 page
作为依赖项传递。
2)notification
问题的解决方案:
使用 setState(prevState => newState)
.
解法:
const fetchMoreNotifications = useCallback(async () => {
const newPage = page + 7;
setPage(newPage);
console.log(
"FETCH MORE from page " + newPage + " on array of " + notifications.length
);
getNotifications(newPage).then(() => { // <---- Pass as param
setIsLoading(false);
});
}, [page]); // <---- Add page as dependency
const getNotifications = useCallback(
async page => { // <---- Get page as a param
setError(null);
setIsLoading(true);
try {
console.log("Get from page " + page);
// let fromRecord = (page - 1) * 7;
const retrievedNotifications = await getNotificationList(
page,
7,
true,
false
);
setNotifications(prevNotification => prevNotification.concat(
retrievedNotifications &&
retrievedNotifications.response &&
retrievedNotifications.response.notifications
)); // <---- Setting up state directly from previous value, instead of getting it from clone version of use callback
} catch (err) {
console.log(err);
setError(err.message);
}
setIsLoading(false);
},
[setIsLoading, setNotifications, setError]
);
工作演示:
检查控制台日志以获取更新的页面值,通知将在 Html 上自行呈现
NOTE : Removed some of your code just to improve code readability and debug the issue