避免递归重新渲染

时间:2020-10-11 20:57:22

标签: reactjs react-native react-redux

在用户激活拖动刷新或首次访问该组件时,将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>
        );
      }
  }
}

如果任何人有任何想法可以更好地解决这个问题或解决那将是很棒的问题!我想我看代码已经太久了,所以我很无聊地思考如何解决...。

3 个答案:

答案 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}