9-7 RN之首页列表

129 阅读2分钟

在本节中,我们将编写首页的列表组件,并实现列表项的点击事件处理和性能优化。

一、创建 Model

models/home.ts 中添加以下内容:

  1. 声明接口地址

    const CHANNEL_URL = '/mock/11/bear/channel';
    
  2. 定义 IChannel 接口

    export interface IChannel {
      id: string;
      image: string;
      title: string;
      played: number;
      playing: number;
      remark: string;
    }
    
  3. HomeState 中定义 channelList 属性

    export interface HomeModelState {
      carousels: ICarousel[];
      guess: IGuess[];
      channelList: IChannel[];
    }
    
  4. effects 中定义请求频道列表数据

    effects: {
      *fetchChannelList(_, { call, put }) {
        const { data } = yield call(axios.get, CHANNEL_URL);
        yield put({
          type: 'setState',
          payload: {
            channelList: data.results,
          },
        });
      },
    },
    

二、创建列表组件

pages/Home/index.tsx 中编写列表组件:

1. 编写 ChannelItem 组件

import React from 'react';
import { View, Text, Image, StyleSheet, TouchableOpacity } from 'react-native';
import Icon from '@/assets/iconfont/index';
import { IChannel } from '@/models/home';

interface IProps {
  item: IChannel;
  onPress: (item: IChannel) => void;
}

class ChannelItem extends React.PureComponent<IProps> {
  onPress = () => {
    const { item, onPress } = this.props;
    onPress(item);
  };

  render() {
    const { item } = this.props;
    return (
      <TouchableOpacity onPress={this.onPress} style={styles.container}>
        <View style={styles.item}>
          <Image source={{ uri: item.image }} style={styles.image} />
          <View style={styles.rightView}>
            <Text style={styles.titleText} numberOfLines={2}>
              {item.title}
            </Text>
            <Text style={styles.remarkText} numberOfLines={2}>
              {item.remark}
            </Text>
            <View style={styles.bottomView}>
              <View style={styles.playedView}>
                <Icon name="icon-V" size={14} color="#f86442" />
                <Text style={{ marginLeft: 5 }}>{item.played}</Text>
              </View>
              <View style={styles.playingView}>
                <Icon name="icon-shengyin" size={14} color="#f86442" />
                <Text style={{ marginLeft: 5 }}>{item.playing}</Text>
              </View>
            </View>
          </View>
        </View>
      </TouchableOpacity>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#fff',
    margin: 10,
    borderRadius: 8,
    shadowOffset: { width: 0, height: 5 },
    shadowOpacity: 0.5,
    shadowRadius: 10,
    shadowColor: '#ccc',
    elevation: 80,
  },
  item: {
    flexDirection: 'row',
    padding: 10,
  },
  image: {
    width: 100,
    height: 100,
    borderRadius: 8,
    marginRight: 10,
    backgroundColor: '#dedede',
  },
  rightView: {
    flex: 1,
  },
  titleText: {
    fontSize: 16,
    marginBottom: 10,
  },
  remarkText: {
    backgroundColor: '#f8f8f9',
    padding: 5,
  },
  bottomView: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'flex-end',
  },
  playedView: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  playingView: {
    flexDirection: 'row',
    alignItems: 'center',
  },
});

export default ChannelItem;

2. 在 Home 组件中使用 FlatList

import React from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import { connect, ConnectedProps } from 'react-redux';
import { RootState } from '@/models/index';
import ChannelItem from './ChannelItem';

interface IProps extends ConnectedProps<typeof connector> {
  onPress: (item: IChannel) => void;
}

class Home extends React.PureComponent<IProps> {
  componentDidMount() {
    this.loadChannelList();
  }

  loadChannelList = () => {
    const { dispatch } = this.props;
    dispatch({
      type: 'home/fetchChannelList',
    });
  };

  renderItem = ({ item }: { item: IChannel }) => {
    return (
      <ChannelItem
        item={item}
        onPress={(item) => this.props.onPress(item)}
      />
    );
  };

  render() {
    const { channelList } = this.props;
    return (
      <View style={styles.container}>
        <FlatList
          style={styles.scrollview}
          data={channelList}
          renderItem={this.renderItem}
          keyExtractor={(item) => item.id}
        />
      </View>
    );
  }
}

const mapStateToProps = ({ home }: RootState) => ({
  channelList: home.channelList,
});

const connector = connect(mapStateToProps);

export default connector(Home);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  scrollview: {
    flex: 1,
  },
});

三、性能优化

1. 使用 PureComponent

ChannelItem 组件继承自 React.PureComponent,以避免不必要的组件重渲染。

2. 使用 React.memo

对于函数组件,可以使用 React.memo 进行优化:

const Touchable = React.memo((props: TouchableOpacityProps) => (
  <TouchableOpacity activeOpacity={0.8} {...props} />
));

3. 使用 keyExtractor

FlatList 中使用 keyExtractor 函数,为每个列表项生成唯一的 key

keyExtractor={(item) => item.id}

四、总结

在本节中,我们完成了首页列表的开发,包括数据请求、列表展示、点击事件处理和性能优化。通过合理的组件拆分和性能优化,可以显著提升应用的性能和用户体验。下一节,我们将继续完善首页的功能。