javascript - ListFooterComponent 无法正常工作?

标签 javascript reactjs react-native react-native-flatlist

我对 Flatlist 有一个奇怪的问题, 当我向下滚动时,我从 API 获取数据,我增加页面 +1 并在页脚中渲染一个微调器,但是当最后一页 == 当前页面时,这意味着没有数据出现,但微调器卡在底部,尽管我更新就假了! 那么这里出了什么问题!

顺便说一句 当我在 FlatList 中像这样调用 renderFooter 时

ListFooterComponent={()=> this._renderFooter()} // it disappeare the bottom spiner if last page == current page but I have an unexpected re-rendering and app laggay and in some time spinner disappeared even I scrolling to bottom!!

代码

class LastSongs extends React.PureComponent {
  constructor() {
    super();
    this.state = {
      songs: [],
      loading: false,
      page: 1,
      last_page: 1,
    };
    this.isCancelled = false;
  }

  manipulateArray = async (array) => {
    let songs = [];
    array.map((track) =>
      songs.push({
        id: track.id,
        name: track.name,
        url: URL + track.sounds,
        img: URL + track.avatar,
      }),
    );
    return songs;
  };

  getData = async () => {
    try {
      this.setState({loading: true});
      let response = await API.get(`/tracks?page=${this.state.page}`);
      let lastPage = response.data.data.items.last_page;
      let {
        data: {
          data: {
            items: {data},
          },
        },
      } = response;
      let All_Songs = await this.manipulateArray(data);
      this.setState({
        songs: this.state.songs.concat(All_Songs),
        last_page: lastPage,
      });
    } catch (err) {
      console.log('err', err);
    }
  };

  _renderItems = ({item, index}) => (
    <TouchableNativeFeed
      key={item.id}
      onPress={() => {
        this.props.saveSongs(this.state.songs, index);
        this.props.isPlaying(true);
        this.props.isPauseTrigger(!this.props.isPauseTrigger);
      }}
      background={TouchableNativeFeedback.Ripple('white')}
      delayPressIn={0}
      useForeground>
      <Card
        style={{
          justifyContent: 'center',
          alignItems: 'center',
          backgroundColor: '#121212',
          flex: 1,
        }}>
        <FastImage
          source={{uri: item.img}}
          resizeMode={FastImage.resizeMode.cover}
          style={styles.cardImg}
        />
        <Body style={{...styles.cardItem, width: '100%'}}>
          <View style={styles.radioCardName}>
            <Text style={styles.text} numberOfLines={1}>
              {item.name}
            </Text>
          </View>
        </Body>
      </Card>
    </TouchableNativeFeed>
  );

  handleLoadMore = () => {
    if (this.state.page <= this.state.last_page - 1) {
      this.setState({loading: false, page: this.state.page + 1}, () =>
        setTimeout(() => {
          this.getData();
        }, 800),
      );
    } else if (this.state.page === this.state.last_page) {
      this.setState({loading: false}, () =>
        console.log('if--loading', this.state.loading),
      );
    }
  };

  _renderFooter = () => {
    return this.state.loading ? (
      <View style={styles.loader}>
        <ActivityIndicator color="#ebff64" size="large" />
      </View>
    ) : null;
  };
  componentDidMount() {
    this.getData();
  }

  _keyExtractor = (song) => song.id;

  _renderListEmptyComponent = () => <EmptyList />;
  render() {
    console.log('App rerender!');
    return (
      <Container style={styles.container}>
        <Header
          style={styles.darkHeader}
          androidStatusBarColor="#121212"
          iosBarStyle="light-content">
          <Left>
            <Button transparent onPress={() => this.props.navigation.goBack()}>
              <Icon name="ios-arrow-forward" style={styles.colorWhite} />
            </Button>
          </Left>
          <Body>
            <Title style={styles.headerText}>اخر الاغاني</Title>
          </Body>
          <Right></Right>
        </Header>
        <FlatList
          data={this.state.songs}
          keyExtractor={this._keyExtractor}
          initialNumToRender={10}
          contentContainerStyle={styles.contentContainerStyle}
          columnWrapperStyle={styles.columnWrapperStyle}
          numColumns={2}
          ListEmptyComponent={this._renderListEmptyComponent}
          renderItem={this._renderItems}
          onEndReached={this.handleLoadMore}
          onEndReachedThreshold={0.7}
          ListFooterComponent={this._renderFooter}
          removeClippedSubviews={true}
          maxToRenderPerBatch={100} // Increase time between renders
        />
      </Container>
    );
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    isPlaying: (_isPlaying) => {
      dispatch(isPlayingAction(_isPlaying));
    },

    isPauseTrigger: (_isPause) => {
      dispatch(isPauseAction(_isPause));
    },

    saveSongs: (songs, index) => {
      dispatch(saveSongsPlayer(songs, index));
    },
  };
};

export default connect(null, mapDispatchToProps)(LastSongs);

最佳答案

当您滚动到底部时,您正在调用一个超时的异步函数。该超时将覆盖您的以下代码并再次将加载设置为 true。所以在这种情况下加载永远不会错误。

 } else if (this.state.page === this.state.last_page) {
      this.setState({loading: false}, () =>
        console.log('if--loading', this.state.loading), // log false!! that's mean a spinner should disapeare
      );
 }

这里你需要两件事。

1) 尝试在catch block 中将loading 设置为false。

} catch (err) {
   console.log('err', err);
   this.setState({loading: false});
}

2) 在状态 isAllDataFetched 中添加另一个值,初始值为 false。当您从 API 收到空数据时,将 loading 设置为 false。不确定您的数据是什么样子,但执行类似的操作;

getData = async () => {
    try {
      this.setState({loading: true});
      let response = await API.get(`/tracks?page=${this.state.page}`);
      let lastPage = response.data.data.items.last_page;
      let {
        data: {
          data: {
            items: {data},
          },
        },
      } = response;

      // if it's an array
      if(data.length === 0) {
         this.setState({loading: false, isAllDataFetched: true});
      }
      //...
    } catch (err) {
      console.log('err', err);
    }
  };

最后,在您的 handleLoadMore 方法中添加以下行。

handleLoadMore = () => {
 if(this.state.isAllDataFetched) return;
<小时/>

我为您创建了一个演示。您可以遵循这个逻辑来使其发挥作用。它与您所拥有的有点不同,但我认为它会有所帮助。

这是代码。

import React from 'react';
import {
  View, Text, FlatList, ActivityIndicator, SafeAreaView
} from 'react-native';


class App extends React.PureComponent {
  state = {
    songs: [
      {
        userId: 1,
        id: 1,
        title: 'delectus aut autem 1',
        completed: false,
      },
      {
        userId: 1,
        id: 2,
        title: 'delectus aut autem 2',
        completed: false,
      },
    ],
    loading: false,
    page: 3,
    totalPage: 10,
  };

  componentDidMount() {
    this.getData();
  }

  getData = async () => {
    const { songs } = this.state;
    try {
      this.setState({ loading: true });
      const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${this.state.page}`, {
        headers: {
          'Content-Type': 'application/json',
        },
      });
      const json = await response.json();

      this.setState({
        songs: [...songs, json],
      });
      this.setState({ loading: false });
    } catch (err) {
      console.log('err', err);
      this.setState({ loading: false });
    }
  };

  renderItems = ({ item }) => (
    <Text style={{ backgroundColor: 'blue', height: 200, marginBottom: 5 }}>{`${item.title}-${item.id}`}</Text>
  );

  onEndReached = () => {
    const { page, loading, totalPage } = this.state;

    if (loading) return;

    if (page <= totalPage) {
      this.setState({ loading: true, page: page + 1 }, () =>
        setTimeout(() => {
          this.getData();
        }, 2000));
    } else {
      this.setState({ loading: false });
    }
  }

  renderFooter = () => {
    const { loading } = this.state;

    if (loading) {
      return (
        <View>
          <ActivityIndicator color="#000" size="large" />
        </View>
      );
    }
    return null;
  }

  renderListEmptyComponent = () => <View />;

  render() {
    const { songs } = this.state;
    return (
      <SafeAreaView style={{ flex: 1, backgroundColor: 'red' }}>
        <FlatList
          data={songs}
          keyExtractor={song => song.id}
          initialNumToRender={10}
          contentContainerStyle={{ flexFrow: 1, backgroundColor: 'white' }}
          style={{ flex: 1 }}
          ListEmptyComponent={this.renderListEmptyComponent}
          renderItem={this.renderItems}
          onEndReached={this.onEndReached}
          onEndReachedThreshold={0.7}
          ListFooterComponent={this.renderFooter}
        />
      </SafeAreaView>
    );
  }
}
export default App;

并且可以使用工作演示 here (使用iOS设备)

关于javascript - ListFooterComponent 无法正常工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61058796/

相关文章:

javascript - 如何使用 webpack 导入静态 url

javascript - 来自 Firebase 的 2 个或多个 $scope 的数学函数,例如 sum、multiple 等

android-studio - 如何从 Windows 上的克隆存储库运行 React-Native-Navigation v2 Playground?

javascript - 在 React 门户中呈现 iFrame

javascript - 为什么在通过 setTimeout 更新状态之前评估三元运算符的不可到达部分?

android - React Native PushNotification onNotification 方法

java - Google Maps SDK for Android : Smoothly animating the camera to a new location, 沿途渲染所有图 block

javascript - 添加到 JSON 字符串

javascript - 我在 React 中触发此类列表切换时遇到问题

javascript - React - 输入类型文件 Semantic UI React