当用户激活拖动刷新或首次访问组件时,向API发送react-redux操作以返回帖子。
这通常不会引起问题,因为FlatList会直接从道具中获取,因此不会触发递归循环。但是,我需要格式化数据以适应应用程序,因此,在数据到达FlatList之前,我通过一系列方法传递数据。此进程与componentDidUpdate
发生冲突,从而将应用程序抛出一个请求循环。以下代码:
componentDidUpdate(prevProps) {
if (prevProps.posts !== this.props.posts){
this.storePosts() //the problem is that when storeposts is called it is updating the props which then is calling store posts which is updating the props and so on and so on....
}
}
storePosts() { //the problem with this is that it changes the state
this.props.fetchPosts() //this line is causing a continous request to rails
let itemData = this.props.posts
if (itemData !== this.state.currentlyDisplayed) {
this.setState({ currentlyDisplayed: this.props.posts, items: itemData }, () => {
this.setState({isLoading: false})
});
}
}
formatItems = () => {
const itemData = this.props.posts
const newItems = [itemData]
const numberOfFullRows = Math.floor(itemData.length / 3);
let numberOfElementsLastRow = itemData.length - (numberOfFullRows * 3);
while (numberOfElementsLastRow !== 3 && numberOfElementsLastRow !== 0) {
newItems.push({ key: `blank-${numberOfElementsLastRow}`, empty: true });
numberOfElementsLastRow++;
}
return this.setState({ newItems: newItems})
// console.log(newItems.length, numberOfElementsLastRow)
};
renderItem = ({ item, type }) => {
const { items } = this.state;
if (item.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
} else {
return (
<TouchableOpacity style={styles.item} onPressIn={() => this.setState({ itemSelected: item.id })} onPress={() => this.props.navigation.navigate('View Post', {post: item})} key={item.id}>
<Image source={{ uri: item.image }} style={{ flex: 1, width: '100%', height: undefined }} />
</TouchableOpacity>
);
}
};
onRefresh = () => {
this.setState({refreshing: true})
this.storePosts()
this.setState({refreshing: false, currentlyDisplayed: this.props.posts})
};
render() {
const { error: { vaultErrorMessage }} = this.props
const { posts } = this.props
<SafeAreaView>
<FlatList
data={this.state.currentlyDisplayed}
renderItem={this.renderItem}
numColumns={3}
keyExtractor={(item, index) => index.toString()}
refreshControl={<RefreshControl refreshing={this.state.refreshing} onRefresh={() => this.onRefresh()} />}
extraData={this.state.refresh}
/>
</SafeAreaView>
);
}
}
}
如果有人有任何想法可以更好地解决这个问题,那就太好了!我想我已经看代码太久了,所以我对如何解决这个问题很迟钝。。。。
我建议将更新posts和存储posts的逻辑拆分为两个单独的方法,以避免无限状态更新。
例如:
componentDidUpdate(prevProps) {
if (shouldUpdate) { // some other condition
this.updatePosts();
}
if (prevProps.posts !== this.props.posts){
this.storePosts();
}
}
updatePosts() {
this.props.fetchPosts();
}
storePosts() {
let itemData = this.props.posts;
if (itemData !== this.state.currentlyDisplayed) {
this.setState({ currentlyDisplayed: this.props.posts, items: itemData }, () => {
this.setState({isLoading: false})
});
}
}
您还应该寻找一种更好的方法来检查帖子是否真的发生了变化,因为数组可能已经改变,但内容可能保持不变。(注意,[1, 2, 3] === [1, 2, 3]
的计算结果为false
(。fast-deep-equal
是一个很好的库,或者您可以想出一个自定义的解决方案。
如果必须使用此方法,请使用static getDerivedStateFromProps
而不是componentDidUpdate
。
getDerivedStateFromProps
在调用渲染方法之前立即调用,无论是在初始装载还是在后续更新中。它应该返回一个对象来更新状态,或者返回null
来不更新任何内容。这种方法适用于状态取决于道具随时间变化的罕见用例。例如,实现一个
<Transition>
组件可能很方便,该组件可以比较其上一个子级和下一个子级,以决定要在其中设置动画。
您可以尝试一下;
extraData={this.state.currentlyDisplayed}