【React-native】 实现分类左右菜单联动及不联动自定义

提示:React-native类商城分类联动


效果展示

联动菜单
非联动自定义菜单

提示:以下是本篇文章正文内容,下面案例可供参考

具体案例地址

案例地址

组件主要代码

renderRootCate() {//左边分类列表  
    const { rootCateData, bgColor } = this.state
    const { rootEndReached } = this.props
    return (
      <View style={{ backgroundColor: bgColor }}>
        <FlatList
          ref={flatList => this.flatList = flatList}
          data={rootCateData}
          ListHeaderComponent={() => (<View />)}
          ListFooterComponent={() => (<View />)}
          // ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: '#F5F5F5' }} />}
          renderItem={(e) => this.renderRootItem(e.item, e.index)}
          onEndReachedThreshold={20}
          showsVerticalScrollIndicator={false}
          onEndReached={() => rootEndReached()}
        />
      </View>
    )
  }
renderRootItem = (item, index) => {//左边菜单节点
    const { rootCateData, selectedRootCate, mainColor, bgColor } = this.state
    const { needScrollTo } = this.props
    return (
      <View style={{ position: 'relative' }}>
        {
          selectedRootCate !== index && <View style={{ width: ROOT_WIDTH, height: ROOT_HEIGHT, backgroundColor: mainColor, position: 'absolute', zIndex: -1 }} />
        }
        <TouchableOpacity
          key={index}
          style={[{ alignItems: 'center', justifyContent: 'center', width: ROOT_WIDTH, height: ROOT_HEIGHT },
          index === selectedRootCate + 1 ? { borderTopRightRadius: ROOT_RADIUS } : {},
          index === selectedRootCate - 1 ? { borderBottomRightRadius: ROOT_RADIUS } : {},
          index === selectedRootCate ?
            { backgroundColor: mainColor, borderTopLeftRadius: ROOT_RADIUS, borderBottomLeftRadius: ROOT_RADIUS }
            : { backgroundColor: bgColor }
          ]}
          onPress={() => {
            setTimeout(() => {
              (rootCateData.length - index) * ROOT_HEIGHT > screenH - HEADER_HEIGHT - ROOT_HEIGHT ? this.flatList.scrollToOffset({ animated: true, offset: index * ROOT_HEIGHT }) : null
              this.sectionList.scrollToLocation({ itemIndex: 0, sectionIndex: needScrollTo ? index : 0, animated: true, viewOffset: 0 })
            }, 100)
            this.setState({ selectedRootCate: index, rootSelItem: item, isScroll: false })
          }}
        >
          <Text
            style={{ fontSize: 14, color: '#808080' }}
            numberOfLines={1}
            ellipsizeMode='tail'
          >
            {item.firstCateName}
          </Text>
        </TouchableOpacity>
      </View>
    )
  }
renderItemCate() {//右边菜单
    const { cateData, selectedRootCate, rootSelItem } = this.state
    const { renderSectionHeader, renderItem, needScrollTo, onCateEndReached, onCateRefresh } = this.props
    return (
      <View style={{ flex: 1 }}>
        <SectionList
          ref={(ref) => this.sectionList = ref}
          renderSectionHeader={(item) => renderSectionHeader ? renderSectionHeader(item) : this.sectionComp(item)}
          renderItem={(data) => renderItem ? renderItem(data) : this.renderItem(data)}
          sections={cateData}
          // ListEmptyComponent={<DefaultPage style={{ height: 200 }} />} 
          ItemSeparatorComponent={() => <View />}
          ListHeaderComponent={() => <View />}
          showsVerticalScrollIndicator={false}
          keyExtractor={(item, index) => 'key' + index + item}
          onEndReached={() => onCateEndReached && onCateEndReached()}
          onViewableItemsChanged={(e) => { needScrollTo && this.scrollRootSel(e) }}
          onScrollBeginDrag={(e) => { this.setState({ isScroll: true }) }}
          onScrollAnimationEnd={() => { this.setState({ isScroll: false }) }}
        />
      </View>
    )
  }
renderItem(item) {//右边菜单二级分类
    const { cateData, } = this.state
    const { renderCell, needScrollTo } = this.props
    let sectionIndex = item.section.rootIndex
    let data = item?.section?.data
    return item.index === 0 ?
      <>
        <View key={item.index}
          style={{
            paddingLeft: 24,
            flexDirection: 'row',
            alignItems: 'center',
            flexWrap: 'wrap'
          }}
        >
          {
            data.map((cell, index) => renderCell ? renderCell(cell, sectionIndex, index) : this.renderCell(cell, sectionIndex, index))
          }
        </View>
        {
          needScrollTo && <>
            {
              item?.section?.rootIndex !== cateData?.length - 1 ?
                <View style={{ height: 1, backgroundColor: '#efefef' }} />
                : <View style={{ height: screenH - (Math.ceil(item?.section?.data?.length / SECTION_COLUMS) * CELL_HIGHT + SECTION_HEADER) - HEADER_HEIGHT }} />//最后一个菜单的空白占位
            }
          </>
        }
      </>
      : null
  }
scrollRootSel = (e) => {//右边菜单滚动时左边菜单跳到相应选项
    const { cateData, isScroll, rootCateData } = this.state
    let currentIndex = e?.viewableItems[0]?.section?.rootIndex
    if (isScroll) {
      this.setState((preveState, preveProps) => ({
        selectedRootCate: currentIndex,
        rootSelItem: cateData[currentIndex]
      }), () => {
        (rootCateData.length - currentIndex) * ROOT_HEIGHT > screenH - HEADER_HEIGHT ? this.flatList.scrollToOffset({ animated: true, offset: currentIndex * ROOT_HEIGHT }) : null
      })
    }
  }

使用

render() {//左右联动菜单
    return (

      <View style={[{ flex: 1, paddingTop: 10 }]}>
        <CategoryList
          rootCateData={data.rootCateData}
          cateData={data.cateData}
          rootEndReached={() => { }}
          itemOnPress={(item, sectionIndex, index) => alert(`点击了第${sectionIndex}组中的第${index}个商品`)}
          needScrollTo
        // onCateEndReached={() => {}}
        />
      </View>
    )
  }

 render() {//自定义非联动菜单
    const { selectedRootCate } = this.state
    return (
      <View style={{ flex: 1, paddingTop: 10, backgroundColor: '#fff' }}>
        <CategoryList
          bgColor='#fff'
          mainColor='#f5f5f5'
          rootCateData={data.rootCateData}
          rootEndReached={() => { }}
          cateData={[data.cateData[selectedRootCate]]}
          renderCell={(item, sectionIndex, index) => this.renderCell(item, sectionIndex, index)}
          renderSectionHeader={(item) => this.sectionComp(item)}
          selRootCateChange={(index, item) => { this.setState({ selectedRootCate: index }) }}
        />
      </View >
    )
  }

数据格式

提示:一般右边菜单都是根据左边选中的再进行请求,因此左右菜单的数据是分开传的

{
  "rootCateData":[
    {
      "firstCateName":"香蕉",
      "firstCateId":0

    }
    ,{
      "firstCateName":"苹果",
      "firstCateId":1
    }
    ,{
      "firstCateName":"车厘子",
      "firstCateId":2
    }
  ],
  "cateData":[
    {
      "rootIndex":0,
      "secondCateName":"香蕉",
      "data":[
        {
          "id":100010,
          "img":"",
          "name":"本地蕉",
          "sectionId":0
        }
        ,{
          "id":100011,
          "img":"",
          "name":"国产蕉",
          "sectionId":0
        }
      ]
    }
    ,{
      "rootIndex":1,
      "secondCateName":"苹果",
      "data":[
        {
          "id":100020,
          "img":"",
          "name":"红苹果",
          "sectionId":1
        }
        ,{
          "id":100021,
          "img":"",
          "name":"青苹果",
          "sectionId":1
        }
      ]
    }
    ,{
      "rootIndex":2,
      "secondCateName":"车厘子",
      "data":[
        {
          "id":100030,
          "img":"",
          "name":"车厘子A",
          "sectionId":2
        }
        ,{
          "id":100031,
          "img":"",
          "name":"车厘子JJ",
          "sectionId":2
        }
      ]
    }
  ]
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-雾里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值