最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react-native 封装城市选择组件(二)

    正文概述 掘金(吴敬悦)   2021-01-09   621

    今天我进一步进行完善,主要是把暴露的接口写完了,我还会进一步完善,如果在项目中跑起来没什么问题的话,我就会上传到 npm ,以供大家学习和使用,当然如果能封装更好的,大家可以一块学习。

    一、props

    下面看 props

    1. data 这个参数必传。跟 SectionList 相似,只不过我的数组属性是 items , 而 SectionList 的是 data
    [
    	{
        	...
            items: []
        }
    ]
    
    1. selectedIndexes 初始化已选中的下标,如:
    // 这里面的元素个数取决于你的总列数
    [0, 0, 0] // 代表选中第一列的第一个,第二列的第一个,第三列的第一个
    [2, 1, 0] // 代表选中第一列的第三个,第二列的第二个,第三个的第一个
    // 加入说你传入的是 [1] 那么就会选中第一列的第二个,第二列的第二个,第三列的第二个,也就相当于 [1, 1, 1]
    // 如果传入的是 [1, 2] 那么就会选中第一列的第二个,第二列的第三个,第三列的第三个,也就相当于 [1, 2, 2]
    // 如果不传,就默认: [0, 0, 0]
    
    1. renderItems, renderItem 这个参数必传。这个就是 FlatListrenderItem ,这两个的优先级 renderItems > renderItem
    // 如果传入的 renderItem 而没有传入 renderItems ,
    // 那么所有列都使用相同的 renderItem ,否则按照 renderItems 数组里面的渲染各自的
    // 先假设总的列数是 n
    // 渲染规则跟上面的 selectedIndexes 相同
    // 比如 [()=>null] ,相当于 [()=>,()=>null,()=>null]
    // 比如 [()=>null,()=><Text>test</Text>] ,相当于 [()=>null,()=><Text>test</Text>, ()=><Text>test</Text>]
    // 值得说的是回调的参数
    // ({item, index, isSelected}) => null
    // 其中 item 代表每一列的每一项数据
    // index 代表下标
    // isSelected 代表当前行是否被选中
    
    1. n 这个就是代表总列数,这个参数必传
    2. style 最外面容器的样式。
    3. widthRadio 这个是每一列的宽度占比。
    // 比如: ['30%', '30%', '30%'],那么就代表三列的宽度都是屏幕宽度的一半
    // 比如: [240, '40%'],那么代表第一列 240 ,第二列占屏幕的宽度百分之四十
    
    // 如果有缺省值,也就是传入 n > 数组的长度,比如 n 我传入的是 5 ,而数组 ['30%', '30%', '30%'] ,
    // 那么相当于 ['30%', '30%', '30%', '30%', '30%'] 这样的话有些列就不会在屏幕上显示,只需要滑动就能看到
    
    1. flatListContainerStyle 这个代表所有列的容器的样式。
    2. ListHeaderComponent 跟 FlatList 相似,只不过不同的是这个不会随着列表的上下滑动而滑动。
    3. ListFooterComponent 同上。
    4. styles 这个代表每一个 FlatListstyle ,目前的功能是一一对应的,以后也会像上面的 selectedIndexes 一样。
    5. moreFlatListProps 这个是 FlatListprops ,也是一一对应的,同样以后会实现像上面的效果。
    6. scrollViewProps 这个就是 ScrollViewprops
    7. itemPress 这个就是每一项的点击事件。
    // itemPress 回调的参数就是每一列的数据,除 items 外的数据,像下面实现:
    const itemPress = (item, i) => {}
    // 这里面的 i 是代表第几列,第一列是 0 以此类推。
    

    二、效果图

    react-native 封装城市选择组件(二)

    三、示例代码

    const DefaultPicker = (props) => {
      const renderItemTitle = useCallback(({item, index, isSelected}) => {
        const style = {
          borderLeftWidth: 4,
          borderLeftColor: isSelected ? colors.primary : '#fff',
          backgroundColor: isSelected ? '#F8F8F8' : '#fff',
        };
        return (
          <View style={style}>
            <Text key={index} style={styles.itemTitleStyle}>
              {item?.name || ''}
            </Text>
          </View>
        );
      }, []);
      const renderItemOther = useCallback(({item, index, isSelected}) => {
        const style = {
          color: isSelected ? colors.primary : '#505050',
        };
        return (
          <Text key={index} style={[styles.itemOtherStyle, style]}>
            {item?.name || ''}
          </Text>
        );
      }, []);
      return (
        <View style={styles.fullscreen}>
          <View style={styles.fullscreen} />
          <Picker
            {...props}
            style={styles.pickerStyle}
            styles={[styles.f1BgColor, styles.f23BgColor, styles.f23BgColor]}
            ListHeaderComponent={() => ListHeaderComponent(props.disappear)}
            renderItems={[renderItemTitle, renderItemOther]}
            n={3}
            moreFlatListProps={[
              {keyExtractor: (item, index) => item + index},
              {keyExtractor: (item, index) => item + index},
              {keyExtractor: (item, index) => item + index},
            ]}
          />
        </View>
      );
    };
    

    四、源码

    import React, {useCallback, useState} from 'react';
    import {
      ScrollView,
      View,
      StyleSheet,
      FlatList,
      Dimensions,
      Pressable,
    } from 'react-native';
    
    const selectData = (index, indexes, data) => {
      let tempData = data;
      for (let i = 0; i < index; i++) {
        tempData = tempData?.[indexes[i]]?.items || [];
      }
      return tempData;
    };
    
    /**
     * 初始化已经选择的数组
     * @param {Array<number>} selectedIndexes 传入的已经选择的下标
     * @param {number} n 列的个数
     */
    const initSelectedIndexes = (selectedIndexes = [], n) => {
      const tempIndexes = [...selectedIndexes];
      for (let i = selectedIndexes?.length || 0; i < n; i++) {
        tempIndexes.push(0);
      }
      return tempIndexes;
    };
    
    /**
     * 得到模拟数组
     * @param {number} n 所有数据
     */
    const getSimulateArray = (n) => {
      return new Array(n).fill(1);
    };
    
    /**
     *
     * @param {Array<number|string>} widthRadio 各宽度比例
     */
    const getWidths = (widthRadio, n) => {
      const width = Dimensions.get('screen').width;
      const len = widthRadio?.length || 0;
      const tempWidths = [];
      for (let i = 0; i < len; i++) {
        if (typeof widthRadio[i] === 'string') {
          const radio = parseFloat(widthRadio[i]) / 100;
          tempWidths.push(width * radio);
        } else {
          tempWidths.push(widthRadio[i]);
        }
      }
    
      // 如果还有没有添加到数组中的就默认使用最后一个宽度,如果前面都没有值
      // ,那么就定义宽度为总宽度的 1/3
      const equalWidth =
        tempWidths[tempWidths.length - 1] || parseFloat((width / 3).toFixed(2));
      for (let j = len; j < n; j++) {
        tempWidths.push(equalWidth);
      }
      return tempWidths;
    };
    
    const getComponent = (ele = null) => {
      let temp;
      if (typeof ele === 'function') {
        temp = ele;
      } else {
        temp = () => ele;
      }
      return temp;
    };
    
    const getRenderItem = (renderItems = [], n) => {
      if (!renderItems) {
        throw new Error('您至少传入一个 renderItem');
      }
      const tempRenderItems = [];
      if (Array.isArray(renderItems)) {
        tempRenderItems.push(...renderItems);
      } else {
        tempRenderItems.push(renderItems);
      }
      const len = tempRenderItems.length;
      const sameRenderItem = tempRenderItems[len - 1];
      for (let i = len; i < n; i++) {
        tempRenderItems.push(sameRenderItem);
      }
      return tempRenderItems;
    };
    
    /**
     * @typedef {{
     *  data: Array,
     *  selectedIndexes?: Array<number>,
     *  renderItems: Array<({item: any, index: number, isSelected: boolean}) => React.ReactElement>,
     *  renderItem: {item: any, index: number}) => React.ReactElement,
     *  styles?: Array<import('react-native').ViewStyle>,
     *  style?: import('react-native').ViewStyle,
     *  flatListContainerStyle?: import('react-native').ViewStyle,
     *  widthRadio?: Array<number|string>,
     *  ListHeaderComponent?: () => React.ReactElement | import('react').ReactElement | null,
     *  ListFooterComponent?: () => React.ReactElement | import('react').ReactElement | null,
     *  moreFlatListProps?: Array<import('react-native').FlatListProps>,
     *  scrollViewProps?: import('react-native').ScrollViewProps,
     *  n: number,
     *  itemPress?: (item: any, n: number) => void
     * }} PickerType
     * @param {PickerType} param0
     */
    const Picker = ({
      data,
      selectedIndexes,
      renderItems,
      renderItem,
      style,
      flatListContainerStyle,
      widthRadio,
      ListHeaderComponent,
      ListFooterComponent,
      styles,
      moreFlatListProps,
      scrollViewProps,
      n,
      itemPress,
    }) => {
      const [indexes, setIndexes] = useState(
        initSelectedIndexes(selectedIndexes, n),
      );
    
      const onPress = useCallback((i, index) => {
        setIndexes((oldIndexes) => {
          oldIndexes[i] = index;
          for (let j = i + 1; j < 3; j++) {
            oldIndexes[j] = 0;
          }
          return [...oldIndexes];
        });
      }, []);
      const toRenderItem = useCallback(
        ({item, index}, i) => {
          const setItem = {...item};
          delete setItem.items;
          return (
            <Pressable
              onPress={() => {
                onPress(i, index);
                itemPress?.(setItem, i);
              }}>
              {getRenderItem(renderItems || renderItem, n)[i]({
                item,
                index,
                isSelected: indexes[i] === index,
              })}
            </Pressable>
          );
        },
        [renderItems, renderItem, n, indexes, onPress, itemPress],
      );
      return (
        <View style={[pickerStyles.container, style]}>
          {getComponent(ListHeaderComponent)()}
          <ScrollView {...scrollViewProps} horizontal={true}>
            <View style={[pickerStyles.listContainer, flatListContainerStyle]}>
              {getSimulateArray(n).map((_, index) => {
                return (
                  <FlatList
                    {...(moreFlatListProps?.[index] || {})}
                    style={[
                      pickerStyles.flatListStyle,
                      styles?.[index] || {},
                      {width: getWidths(widthRadio, n)[index]},
                    ]}
                    data={selectData(index, indexes, data)}
                    renderItem={({item, index: i}) =>
                      toRenderItem({item, index: i}, index)
                    }
                  />
                );
              })}
            </View>
          </ScrollView>
          {getComponent(ListFooterComponent)()}
        </View>
      );
    };
    
    const pickerStyles = StyleSheet.create({
      container: {
        flex: 1,
      },
      listContainer: {
        flex: 1,
        flexDirection: 'row',
        backgroundColor: '#fff',
      },
      flatListStyle: {
        height: '100%',
      },
    });
    
    export default React.memo(Picker);
    

    这篇主要是介绍 props 的和我实战的实例。以后还会完善,最终要达到上传到 npm 的目的。如果以后在使用过程中遇到性能的问题我也会进行优化。


    起源地下载网 » react-native 封装城市选择组件(二)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元