在用户激活拖动刷新或首次访问该组件时,将react-redux操作发送到API以返回帖子。
这通常不会造成问题,因为FlatList将直接从props获取,因此不会触发递归循环。但是,我需要格式化数据以适合应用程序,结果,在数据到达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>
);
}
}
}
如果任何人有任何想法可以更好地解决这个问题或解决那将是很棒的问题!我想我看代码已经太久了,所以我很无聊地思考如何解决...。
答案 0 :(得分:2)
我建议将更新帖子的逻辑划分为两个独立的方法,以避免无限状态更新。
例如:
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
是一个很好的库,或者您可以提出一个自定义解决方案。
答案 1 :(得分:0)
如果必须使用此方法,请使用static getDerivedStateFromProps
而不是componentDidUpdate
。
getDerivedStateFromProps
在初始安装和后续更新上都在调用render方法之前立即被调用。它应该返回一个对象以更新状态,或者返回null
以不更新任何内容。此方法适用于状态依赖于道具随时间变化的罕见用例。例如,实施一个
<Transition>
组件比较上一个和下一个子项以决定要对哪个子对象进行动画制作,可能会很方便。
答案 2 :(得分:0)
您可以尝试;
extraData={this.state.currentlyDisplayed}