【前端工作日志】1-公共组件

前言

我写代码一直有个毛病就是代码耦合性太高,可能是性子太急,总觉得前期设计组件结构会花费太多时间(其实是我能力不足),还有不自信自己最后设计出来的公共组件会提高效率,害怕得不偿失。但是今天遇到了很明显的需要抽离出一个公共组件的情况……我记录下来自己的设计思路,可能不是很准确,欢迎路人善意的指教。

内聚性和耦合性

偶然间刷到知乎的一个关于架构设计的文章,引用其中对我来说印象深刻的观点。详情指路: 【架构设计】如何让你的应用做到高内聚、低耦合?

内聚

关于代码的内聚性通常有7种描述

内聚性
功能内聚顺序内聚通信内聚过程内聚时间内聚逻辑内聚偶然内聚
模块独立性

如果一个页面没有任何公共组件那么就是一个偶然内聚的情况,所有功能集中在一个模块中,这也是我以前写过的,不过在工作中很快就被纠正了,因为实际工作中大多是很多模块有着相同功能,这个时候每个页面都要写一遍的话太费时间了。下面我写的例子我认为可以归为功能内聚。

耦合

耦合性
非直接耦合数据耦合标记耦合控制耦合外部耦合公共耦合内容耦合
模块独立性

技术框架

示例用的是Taro React框架以及TaroUI。
感觉TaroUI很灵活,很多实现可以自己去定制化开发。

需求描述

1、列表搜索功能
2、列表下拉加载,触底分页请求列表数据
上面的需求涉及到了3个不同页面,并且这3个页面的列表接口不是同一个。

设计思路分析

本来是想直接用Taro ReactuseReachBottom这个事件监听滚动列表是否触底的,奈何这个事件并没有监听到,并且usePageScroll这个事件也没有监听到,去github的issue板块查也没有确切的结果,只能用ScrollView滚动视图来替代了,这也是Taro的一个容器组件,只要设置容器高度就可以通过onScrollToLower监听到是否滚动到底部。

首先是父组件引用我写的公共组件CommonScrollView
很明显给子组件传了3个值:
1、service是每个页面的列表请求接口,类型是(params: any)Promise<any>
2、showSearch,boolean 是否存在搜索栏(这个暂时没有不存在的情况,这个目前意义不大)
3、renderList,({ searchVal, dataList }: { searchVal: string, dataList: any[] }) => React.ReactNode这个方法会接收公共组件请求列表接口返回的列表数据,不同页面拿到数据可以在自己的页面写渲染方法,还接收一个search Value,是因为搜索空的空白展示图和普通空白展示图不一样,用来判断展示不同图片的条件。

三个页面的请求参数都是相同的,搜索值(value)、第x页(pageNo)、每页x条(pageSize
所以我干脆把请求参数也放到公共组件里了,不然每个页面还要维护一套pageNo和pageSize的状态,实际公共组件负责更改pageNo(触底+1)

 <CommonScrollView
          service={getProspectusPage}
          showSearch
 >
          {renderList}
 </CommonScrollView>

renderList:

 const renderList = ({searchVal, dataList}: { searchVal: string, dataList: IProspectus[] }) => {
    
    if (!dataList.length) {
      if (searchVal) {
        return (
          <View className={styles.noData}>
          </View>
        )
      }

      return (
        <View className={styles.noData}>
        </View>
      )
    }

    return dataList.map((item: IProspectus) => (
      <View >
      
      </View>
    ))
  }

也就是说我把滚动触底触发分页查询请求,以及搜索触发接口请求和页面初始化时的列表接口请求都放到公共组件CommonScroll里面了,页面只负责拿这个子组件传过来的列表数据进行渲染即可。
而且当我写好一个页面之后,剩下两个页面花了1分钟就解决了!不得不感慨一下前期的组件设计真的很重要!

下面是我公共组件的代码结构


interface IProps {
  children: ({ searchVal, dataList }: { searchVal: string, dataList: any[] }) => React.ReactNode,
  service: (params: any) => Promise<any>,
  showSearch: boolean;
}
const CommonScrollView = (props: IProps) => {
  const { children, service, showSearch = false } = props

  const [searchVal, setSearchVal] = useState<string>('');
  const [currentList, setCurrentList] = useState<any>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [pageNo, setPageNo] = useState<number>(1);
  const [dataReachBottom, setDataReachBottom] = useState<boolean>(false);

  useDidShow(() => {
    fetchData('', 1)
  })

  const fetchData = (val = '', searchPageNo?: number | undefined) => {
    setLoading(true);
    const currentPageNo = searchPageNo || pageNo;
    service?.({
      param: val,
      pageNo: currentPageNo,
      pageSize: 10
    }).then((res: any) => {
      setLoading(false);
      let _currentList = [...currentList, ...res.data.list];
      if(searchPageNo === 1) {

        _currentList = res.data.list;

      } 
      setCurrentList(_currentList);

      if (_currentList.length === res.data.total) {
        setDataReachBottom(true);
      } else {
        setPageNo(currentPageNo + 1)
      }
    });
  }

  const onScrollToLower = () => {
    if (!dataReachBottom) {
      fetchData(searchVal)
      return;
    }
  };



  const handleChange = (val: string) => {
    setSearchVal(val);
  }

  const handleSearch = () => {
    setPageNo(1)
    fetchData(searchVal, 1)
  }
  const handleClear = () => {
    setSearchVal('');
    fetchData('', 1)
  }

  return (
    <Fragment>

      {showSearch && <AtSearchBar
        value={searchVal}
        onChange={handleChange}
        onActionClick={handleSearch}
        onClear={handleClear}
      />}
      <ScrollView
        scrollY
        className='scroll-view'
        scrollWithAnimation
        onScrollToLower={onScrollToLower}
        style={{ height: '80vh' }}
      >
        {children({ searchVal, dataList: currentList })}
        {loading && (
          <View style={{ position: 'relative', height: '5%' }}>

            <AtActivityIndicator content='加载中...' mode='center'></AtActivityIndicator>
          </View>
        )}
        {(dataReachBottom && !searchVal) && (
          <AtDivider
            content='没有更多了'
            fontColor='#676874'
            lineColor='#ECECEC'
          />
        )}
      </ScrollView>
    </Fragment>
  );
}

export default CommonScrollView;

这样看来,如果这些代码在每个页面都写一次,真的很臃肿,用我前同事的话来讲就是:“很僵硬”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值