美文网首页React NativeREACT NATIVEReact Native
学习总结 ~ (三)React Native 初学 之 sec

学习总结 ~ (三)React Native 初学 之 sec

作者: 白公子是猫奴 | 来源:发表于2017-04-23 23:47 被阅读12110次

    目的

    今天我要实现一个 类似于 iOS 开发中带有分组的colllectionView 样式的布局, 每个section都要有个组头。

    首先我们要先决定要使用什么控件。ScrollViewListView/FlatList还有SectionList都是可以选择的。

    • ScrollView 会把所有子元素一次性全部渲染出来。使用上最简单。但是如果你有一个特别长的列表需要显示,可能会需要好几屏的高度。这时就会占用很大的内存去创建和渲染那些屏幕以外的JS组件和原生视图,性能上也会有所拖累。
    • ListView 更适用于长列表数据。它会惰性渲染子元素,并不会立即渲染所有元素,而是优先渲染屏幕上可见的元素。
    • FlatList 是0.43版本开始新出的改进版的ListView,性能更优,但是官方说现在可能不够稳定,尚待时间考验。但是它不能够分组/类/区(section)。
    • SectionList 也是0.43版本推出的, 高性能的分组列表组件。但是它不支持头部吸顶悬浮的效果,但是也不要伤心,官方在下一个版本开始就可以支持悬浮的section头部啦 😜。
      好啦, 综上所诉我选择使用SectionList ,现在开始干活吧 ✌️

    首先


    首先第一步我们先把要显示的样式写好作为子控件, 把数据源整理好。

    例一、
    <SectionList
      renderItem={({item}) => <ListItem title={item.title} />}
      renderSectionHeader={({section}) => <H1 title={section.key} />}
      sections={[ // 不同section渲染相同类型的子组件
        {data: [...], key: ...},
        {data: [...], key: ...},
        {data: [...], key: ...},
      ]}
    />
    
    例二、
    <SectionList
      sections={[ // 不同section渲染不同类型的子组件
        {data: [...], key: ..., renderItem: ...},
        {data: [...], key: ..., renderItem: ...},
        {data: [...], key: ..., renderItem: ...},
      ]}
    />
    

    sections 就是我们的数据源,每一个data 就是我们要用的item, renderItem就是你要显示的子控件哦。如果你每个组都复用一个子组件那就按照例一的结构, 如果你想要不同的组返回不同样式的子组件那就按照例二的结构返回不同的renderItem即可。
    ***这里提个醒, key一定要有, 不同的section 要设置不同的key才会渲染相应的section, 如果你key值都相同, 那可能会出现只显示一组数据的情况哦~ ***

    下面来看看我的代码:
     <SectionList
                            renderItem={this._renderItem}
                            renderSectionHeader={this._renderSectionHeader}
                        sections={[ // 不同section渲染相同类型的子组件
                                { data: [{ title: this.state.appModel[0] }], key: this.state.groupsModel[0].title },
                                { data: [{ title: this.state.appModel[1] }], key: this.state.groupsModel[1].title },
                                { data: [{ title: this.state.appModel[2] }], key: this.state.groupsModel[2].title },
                                { data: [{ title: this.state.appModel[3] }], key: this.state.groupsModel[3].title },
                            ]}
                        />
    
    1.png

    这样有了最基础的样式, 四组纵向的列表, 但是我要横向的, 于是我要设置他的样式啦。

    接下来


    这里我添加两个属性:

      contentContainerStyle={styles.list}//设置cell的样式
      pageSize={4}  // 配置pageSize确认网格数量
    
    const styles = StyleSheet.create({
        list: {
            //justifyContent: 'space-around',
            flexDirection: 'row',//设置横向布局  
            flexWrap: 'wrap',  //设置换行显示
            alignItems: 'flex-start',
            backgroundColor: '#FFFFFF'
        },
    });
    

    好啦, 让我们来看看效果。


    2.png

    😓这是什么鬼???
    为什么它的组头也在左边 , 并且他的其他组数据都横着了, 对于小白我来说只有大写的懵~。不知道你们有没有遇到这种情况, 是什么原因导致的, 我很是困惑啊, 当我把

              renderSectionHeader={this._renderSectionHeader}
    

    这行代码注掉的时候, 它的显示是正常的...


    3.png

    这就尴尬了...
    它的每一个小方块是一个item,达不到我要的效果啊, 于是我决定换个思路, 谁让我是打不死的小白呢😝

    重新来


    我决定让每个section是一个item。在每个item上创建多个可点击的板块。show time ~ ~

     _renderItem = ({ item}) => (
    
            <View  style={styles.list}>
                {
                    item.map((item, i) => this.renderExpenseItem(item, i))
                }
            </View>
    
        );
    
        renderExpenseItem(item, i) {
    
            return <TouchableOpacity key={i} onPress={() => this._pressRow(item)} underlayColor="transparent">
                <View style={styles.row}>
                    <CellView source={item.img}></CellView>
                </View>
            </TouchableOpacity>;
        }
    
        _renderSectionHeader = ({ section }) => (
            <View style={{ flex: 1, height: 25 }}>
                <Text style={styles.sectionHeader} >{section.key}</Text>
            </View>
        );
    
        render() {
            return (
                <View style={{ flex: 1 }}>
                    <Text style={styles.navigatorStyle}> 发现 </Text>
                    <View style={{ flex: 1, backgroundColor: '#F7F6F8' }}>
    
                        <SectionList
                            renderItem={this._renderItem}
                            renderSectionHeader={this._renderSectionHeader}
                            showsVerticalScrollIndicator={false}
                            sections={ // 不同section渲染相同类型的子组件
                                this.state.dataSource
                            }
                        />
                    </View>
                </View>
            );
        }
    }
    
    const styles = StyleSheet.create({
        list: {
            //justifyContent: 'space-around',
            flexDirection: 'row',
            flexWrap: 'wrap',
            alignItems: 'flex-start',
            backgroundColor: '#FFFFFF'
        },
        row: {
            backgroundColor: '#FFFFFF',
            justifyContent: 'center',
            width: (ScreenWidth - 1) / 4,
            height: (ScreenWidth - 1) / 4,
            alignItems: 'center',
        },
        sectionHeader: {
            marginLeft: 10,
            padding: 6.5,
            fontSize: 12,
            color: '#787878'
        },
    });
    

    这里的dataSource 我是在之前数据的基础上又包了一层[],然后在renderItem里做了map映射, 这样每个renderItem上返回了每一组我所需要的子组件。快来看看我的变化吧😊


    4.png

    肿么样, 达到效果了吧, 但是你有没有发现 底部为啥是黄色的?,我可没有去设置这么丑的颜色哦,其实它是提醒我们有不完美的地方, 下面就让我们解决一下这个不完美吧 。

    最后解决问题


    warning.png

    最后让我们来解决问题。它警告我们每个item 要有不同的key ,还记不记得我上面的提醒,我也犯这个错误了。

    • 默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。
    把这个属性添加到      <SectionList/> 里面
           keyExtractor = {this._extraUniqueKey}   
    
             _extraUniqueKey(item ,index){
          return "index"+index+item;
    }  
    
    

    这是每个item要设置key, 同样每个子控件也不能放过, 一定要设置它的key, 要不然这个屎黄色一直伴着你 多烦~~~

    最后看一下我最终的代码吧!

    var Dimensions = require('Dimensions');//获取屏幕的宽高
    var ScreenWidth = Dimensions.get('window').width;
    var ScreenHeight = Dimensions.get('window').height;
    
    // const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);// 这个是创建动画
    export default class Explore extends Component {
    
        constructor(props) {
            super(props);
            this.state = {
                appModel: null,
                groupsModel: null,
                dataSource: null,
            }
        }
    
        //Component挂载完毕后调用
        componentDidMount() {
            this.fetchData();
        }
    
        async fetchData() {
            try {
                let model = await NetFetch.post(_req_url_path, {
    
                });
                let apps = model.apps;
                let groups = model.groups;
    
                let data = [];
                for (let i = 0; i < model.groups.length; i++) {
                    let row = [];
                    for (let j = 0; j < model.apps.length; j++) {
    
                        if (model.groups[i].appIds.indexOf(model.apps[j].appId) >= 0) {
                            row.push(model.apps[j]);
                        }
                    }
    
                    data.push({ data: [row], key: model.groups[i].title });
                }
    // 这里我重组了一下数据结构, 看没看见我在row外面又包了一层, 为了我循环创建每个section的子组件。
    
                this.setState({
                    appModel: model.apps,
                    groupsModel: model.groups,
                    dataSource: data
                });
    
            } catch (error) {
                alert(error.msg);
            }
        }
    
        _renderItem = ({ item}) => (
    
            <View  style={styles.list}>
                {
                    item.map((item, i) => this.renderExpenseItem(item, i))
                }
            </View>
    
        );
    
        renderExpenseItem(item, i) {
    
            return <TouchableOpacity key={i} onPress={() => this._pressRow(item)} underlayColor="transparent">
                <View style={styles.row}>
                    <CellView source={item.img}></CellView>
                </View>
            </TouchableOpacity>;
        }
    
    
        _renderSectionHeader = ({ section }) => (
            <View style={{ flex: 1, height: 25 }}>
                <Text style={styles.sectionHeader} >{section.key}</Text>
            </View>
        );
    
        _listHeaderComponent() {
            return (
                <HeaderView integral={0}></HeaderView>
            );
        }
    
        _listFooterComponent() {
            return (
                <Text style={[styles.remark]}>*预期收益非平台承诺收益,市场有风险,投资需谨慎</Text>
            );
        }
    
        _pressRow(item) {
            this.props.navigator.pushTo(item.go)
        } 
    
        _extraUniqueKey(item ,index){
            return "index"+index+item;
       } 
    
        render() {
            if (!this.state.dataSource) {
                return (
                    <View></View>
                );
            }
    
            return (
                <View style={{ flex: 1 }}>
    
                    <Text style={styles.navigatorStyle}> 发现 </Text>
    
                    <View style={{ flex: 1, backgroundColor: '#F7F6F8' }}>
    
                        <SectionList
                            contentInset={{top:0,left:0,bottom:49,right:0}}// 设置他的滑动范围
                            renderItem={this._renderItem}
                            ListFooterComponent={this._listFooterComponent}
                            ListHeaderComponent={this._listHeaderComponent}
                            renderSectionHeader={this._renderSectionHeader}
                            showsVerticalScrollIndicator={false}
                            keyExtractor = {this._extraUniqueKey}// 每个item的key
                            // contentContainerStyle={styles.list}
                            // horizontal={true}
                            // pageSize={4}  // 配置pageSize确认网格数量
                            sections={ // 不同section渲染相同类型的子组件
                                this.state.dataSource
                            }
    
                        />
                    </View>
                </View>
            );
        }
    
    }
    
    
    const styles = StyleSheet.create({
        navigatorStyle: {
            height: 64,
            backgroundColor: '#FFFFFF',
            textAlign: 'center',
            paddingTop: 33.5,
            fontSize: 17,
            fontWeight: '600',
        },
        list: {
            //justifyContent: 'space-around',
            flexDirection: 'row',
            flexWrap: 'wrap',
            alignItems: 'flex-start',
            backgroundColor: '#FFFFFF'
        },
        row: {
            backgroundColor: '#FFFFFF',
            justifyContent: 'center',
            width: (ScreenWidth - 1) / 4,
            height: (ScreenWidth - 1) / 4,
            alignItems: 'center',
            // borderWidth: 0.5,
            // borderRadius: 5,
            // borderColor: '#E6E6E6'
        },
        sectionHeader: {
            marginLeft: 10,
            padding: 6.5,
            fontSize: 12,
            color: '#787878'
        },
        remark: {
            margin: 10,
            fontSize: 10,
            color: '#D2D2D2',
            marginBottom: 10,
            alignSelf: 'center',
        },
    });
    

    看下最终效果图吧


    完美.jpg

    最最后总结一下在开发中遇到的疑难杂症还有sectionList的重要属性。


    ???.png

    不知道你有没有遇见这个问题, 看起来很简单, 应该是我没有引入Text组件, 但是我确实引入了。最终发现这个问题竟是因为我有段代码是这样写的

               <Image> source={require('../../assets/image/deadline.png')} style={styles.iconStyle} </Image>
                                <Text style={styles.userNameStyle}>赚积分,换好礼!</Text>
    

    不知道你有没有发现错误, 由于习惯我<Image>组件写成<Image></Image>,Image是自封闭标签所以

               <Image source={require('../../assets/image/deadline.png')} style={styles.iconStyle} />
                                <Text style={styles.userNameStyle}>赚积分,换好礼!</Text>
    

    这样问题就解决了, 但是我不清楚它为啥会报这样的错,反正开发中总是会出现一些不知所以的错, 所以平时写代码的时候不断总结起来就好啦...

    SectionList 属性

    • ItemSeparatorComponent?: ?ReactClass<any>
      行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后。

    • ListFooterComponent?: ?ReactClass<any>
      尾部组件

    • ListHeaderComponent?: ?ReactClass<any>
      头部组件

    • keyExtractor: (item: Item, index: number) => string
      此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。

    • onEndReached?: ?(info: {distanceFromEnd: number}) => void
      当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。

    • onRefresh?: ?() => void
      如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing属性。

    • refreshing?: ?boolean
      是否刷新喽

    • renderItem: (info: {item: Item, index: number}) => ?React.Element<any>
      根据行数据data渲染每一行的组件。
      除data外还有第二个参数index可供使用。

    • renderSectionHeader?: ?(info: {section: SectionT}) => ?React.Element<any>
      这就是我用到的每个section的组头

    • sections: Array<SectionT>
      你的数据源

    最后再提醒一下不要忘了key key key 哦 。
    欢迎大家给提意见哦, 里面还是有一些不懂与不足的地方,快用你的见解砸我吧 😘

    相关文章

      网友评论

      • f62385835449:写得真好,赞一个。
      • 702d697f68c3:1:SectionList 如何使用PanResponder 手势回调

        2:SectionList 设置水平滚动,每一组我设置一组图片,每一张图片设置为屏幕宽度,当onEndReached 到第三组图片时候,列表显示变形了,屏幕显示第二组最后一张部分图片和第三组第一张部分图片,也就是说 没有整数倍的显示item .
        702d697f68c3:在SectionList 外面再加一层view ,在view上面监听手势,这个问题用了几天终于被发掘。
        另外一个问题就是,横向的时候,左右滑动 时好时坏,也就是很大几率 SectionList 没接收到左右滑动 的响应,或许被其他啥给覆盖了,但是不知道被其他那个view给覆盖的
      • 六_六:sectionlist加载更多数据的时候,我这边一直显示有问题,楼主有加载更多的代码吗??
        六_六:@tangling 我的问题已经解决,问题原因是SectionList 前套了flatlist 这两个容器都设置了 flex:1的属性,导致数据一值在刷新
        702d697f68c3:同学,你的加载什么问题,
        我的SectionList 容器加载的纯图片, 设置pagingEnabled横向,我的数据都正常, 在加载 多次后, 但是会出现item之间没有按照item倍数显示的效果来,,,
      • HT_Jonson:楼主,json数据是否提供一份
      • fba0f7a00e6a:还有你的最终代码里面// contentContainerStyle={styles.list}
        // horizontal={true}
        // pageSize={4} // 配置pageSize确认网格数量
        这几个属性都注释了.怎么实现的SectionList的横向?!
        白公子是猫奴:@fba0f7a00e6a styles = StyleSheet.create({
        list: {
        //justifyContent: 'space-around',
        flexDirection: 'row',
        flexWrap: 'wrap',
        alignItems: 'flex-start',
        backgroundColor: '#FFFFFF'
        },
        row: {
        backgroundColor: '#FFFFFF',
        justifyContent: 'center',
        width: (ScreenWidth - 1) / 4,
        height: (ScreenWidth - 1) / 4,
        alignItems: 'center',
        },
        sectionHeader: {
        marginLeft: 10,
        padding: 6.5,
        fontSize: 12,
        color: '#787878'
        },
        说的是这个吗
        fba0f7a00e6a:@huang_公子 .......可为何style声明都木有
        白公子是猫奴:@fba0f7a00e6a :cry:我这篇就是写的用自己的布局去实现,而不是用系统的属性啊。
      • fba0f7a00e6a:flexWrap 现在使用它来横向的话会报错,提醒使用 flatlist和它的numColumns属性实现横向.但是flatlist无法分组........请问怎么解决
      • 六听雪碧:你好 我想问下 用了keyExtractor这个 还是提示 missing keys fot items.....这个问题 是怎么回事
        白公子是猫奴:@六听雪碧 这是它的item 设置key. 它的子控件也不能放过, 一定要设置它的key,
      • 1714af606510:sectionList 每个 item 怎么做到 grid 布局,改变数据结构????
        白公子是猫奴:@小七lala flexbox 就可以满足啊。
      • 代码界的扫地僧:renderItem解析出来的 会有section这个字段吗
        白公子是猫奴:@代码界的扫地僧 系统方法都是根据id来的
      • herbsun:楼主 如何做到每个组是不同的item呢
      • 5997854c43de:有点看不懂旧的数据结构和新的数据结构,可以提供数据结构吗?
      • mf168:学习了
      • 耿直boy123:问一下,这个每个item的点击事件该怎么处理
        白公子是猫奴:@耿直boy123 嗯,我这个是一开始出sectionlist 的时候解决这个问题的想法。现在已经更新好几板了。
        耿直boy123:@huang_公子 在_renderItem这一块我用你的实现不了,后来用别人的就能实现了,写法写的跟你不一样,他要去到里面的参数类似是这样的_renderItem = (info) => {
        console.log(info);
        var txt = info.item.title;
        ...
        }而不是你那种每个section 里面又通过一套循环的方式来给每个item填充数据
        白公子是猫奴:@耿直boy123 renderExpenseItem这个方法里的onpress
      • 流星留步:用的是SectionList嵌套FlatList
      • 丑八怪丶:CellView 是什么
        白公子是猫奴:@丑八怪丶 是单独的组件就是你想要的样式,每次循环创建就可以了,这已经是全部的思路了,没有单独的demo.只是项目里遇到的问题。
      • 丑八怪丶:按照你这写完还是有错,能不能提供下源码。
      • 我的昵称是小狼:楼主,,提个建议,,可以使用SectionLIst 嵌套 FlatList
        白公子是猫奴:@我的昵称是小狼 666
      • InnerNight:现在已经0.47啦,SectionList支持头部吸顶了没有?楼主知道吗?
      • geforceyu:感谢提供,请教一个问题,我也是需要sectionlist分区,并且需要两列来显示,你这里是4列嘛,我也遇到了设置pageSize={2},设置contentContainerStyle为横向时,分区和分区头混杂的问题,我看了你的解决方法,还不是很懂,能有更详细的说明吗,或者有更好的方法吗,谢谢
        Sxiaobai:@cwzqf pageSize={2}这样设置当数据源里的个数为奇数时最后一个咋显示在中间 而不是从左边开始排呢
        cwzqf:分区和分区头混杂的问题 可以直接设置头部的width为屏幕的宽度即可
        白公子是猫奴:@geforceyu 我这里没有用那个属性,而是一个item 上去循环创建view
      • b7a302d2c761:求助,我用sectionList的时候,设置的点击每一项的时候改变了state,但是页面不会重新render,这是为什么啊
        b7a302d2c761:@__sev7 sections数据源的地址变化了才会刷新
        __sev7:看看是不是继承了 PureComponent, extraData有没有设置
        白公子是猫奴:@又又切客闹 这个我不知道你setstate 怎么写的了。这个有很多种原因了,也可能跟它的生命周期有关……
      • 啸雨:Hi 哥们,你在高于0.44的ReactNative版本上实现过SectionList吗?SectionSeparatorComponent 会在header上下都创建间隔,如何不让header下面 也就是与item之间 的间隔呢?谢谢~
      • 东海冻鱼:求加微信好友:smile:
      • 593bd475d925:楼主能不能提供源数据格式???谢谢
      • Mars飘殇:问下,sectionList如何设置sectionHeader头部不悬停效果? 还有如何设置某一组sectionHeader头部隐藏,而其他头部仍然显示?
        白公子是猫奴:@Mars飘殇 这个没有哪个特定属性控制,你看看新的版本文档
        Mars飘殇:@huang_公子 那用的是哪个属性呢?
        白公子是猫奴:0.43版本头部是不悬停的, 之后的版本才支持。如果想隐藏头部可以sectionHeader的方法里去做判断吧。
      • 骑着猪的小哥哥:这样子是不是没有复用
        骑着猪的小哥哥:我觉得你这样写数据多的话有内存问题 这种布局一个section 多个item也可以写
        骑着猪的小哥哥:@huang_公子 恩恩 好像SectionList本身也没复用
        白公子是猫奴:@燚公子 一个section 只有一个item了:yum:
      • 楼上那位:楼主写的不错,能加微信吗?
        白公子是猫奴:@楼外楼_山外山别客气,打个赏呗:kissing_heart:
      • 陈秀伊qq:楼主很给力,请问代码在github上有吗? 刚学的小白~
        耀伍杨威:求源码
        白公子是猫奴:@陈秀伊qq 没有,因为是项目中遇到的问题,就随手写了笔记。
      • 于连林520wcf:总结的不错~
        白公子是猫奴:@我吃哈密瓜_d2d9 好的, 等一下发给你
        8869ddfeb528:楼主能不能提供下data的完整数据格式
        白公子是猫奴:@于连林520wcf 谢谢
      • 白公子是猫奴:我已经很满意啦......
        鬼知道我们经历了什么......

      本文标题:学习总结 ~ (三)React Native 初学 之 sec

      本文链接:https://www.haomeiwen.com/subject/aaudzttx.html