美文网首页
useList 列表hook

useList 列表hook

作者: copyLeft | 来源:发表于2020-04-10 23:49 被阅读0次
chart.gif

列表是我们日常开发中经常会碰到的一类展示形式, 只是以不同的 UI 显示在用户面前,例如: 菜单,表格等,其中一些操作, 加载,重置,等是基本相同的,所以我们希望抽离这部分公共逻辑, 这里记录使用hook封装时碰到的一些问题

功能

  • 设置查询参数
  • 设置初始列表值
  • 请求列表,叠加数据
  • 请求列表,重置数据

实现


import React, {
    useState,
    useCallback,
    useRef
} from 'react';

async function asyncVessel(promise) {
    return promise.then(res => [res, null]).catch(err => [null, err])
}

/**
 * 列表请求hook 
 * @param { Function } requery 请求函数 
 * @param { Object } initQuery 初始查询参数 
 * @param { Array } initList 初始列表
 * @returns { Array }
 *  - list 当前列表
 *  - util
 *    - reset 重置设置爱
 *      isEnd 是否已获取所有数据
 *      loadList 请求列表,叠加
 *      query 查询数据 state
 *      currentQuery 查询数据 ref
 *      reloadList 请求类表, 重置
 *      updateQuery 更新查询数据
 * 
 * 
 * @example
 * 
 * const [ list, util ] = useList( load, { sort: 1, type: 2, pageNo: 0, pageSize: 10 } )
 * 
 * 模板
 * 
 * <div>
 *  
 *  <SearchBar onSearch={ util.reloadList } ></SearchBar>
 * 
 *  <List>
 *     { list.map(item =>  <List-item :key={item.id} > { ... } </List-item> ) }
 *  </List>
 * </div>
 * 
 */

function useList(requery, initQuery, initList=[]){

    
    const [ list, setList ] = useState(initList)
    const [ isEnd, setEnd ] = useState(false)
    const [ query, setQuery ] = useState(initQuery)
    const [ queryCache, setQueryCache ] = useState(initQuery)
    const currentQuery = useRef(initQuery)

    const updateQuery = useCallback(
        data => {
            currentQuery.current = { ...currentQuery.current, ...data }
            setQuery( prev => ({...prev, ...data}) )
        },

        [ query, setQuery ]
    )
    
    // 请求列表,叠加数据
    const loadList = useCallback( async () => {

        if(isEnd) return

        currentQuery.current = { ...currentQuery.current, pageNo: currentQuery.current.pageNo + 1 }

        const [ res, err ] = await asyncVessel(requery(currentQuery.current))

        if(err) return

        updateQuery(currentQuery.current)
        
        setEnd( !res.result || !res.result.length)
        setList( prev => [ ...prev, ...res.result ]  )
        
    }, [ isEnd, setEnd, setList, requery ])
    
    
    // 请求列表, 重载数据
    const reloadList = useCallback( async () => {
        
        setEnd(false)

        updateQuery({ pageNo: 1 })
        setList([])

        const [ res, err ] = await asyncVessel(requery(currentQuery.current))

        if(err) return

        setList( [...res.result] )
        
    }, [ isEnd, setEnd, setList, requery ])

    
    // 重置设置
    const reset = useCallback(
        () => {
            setEnd(false)
            updateQuery(queryCache)
        },
        [ setEnd, updateQuery , queryCache]
    )

    const util =  {
        reset,
        isEnd,
        loadList,
        query,
        currentQuery,
        reloadList,
        updateQuery
    }
    
    return [ list, util ]

}

export default useList

问题1 如何设置及更新请求参数

  const [ query, setQuery ] = useState(initQuery)
  const [ queryCache, setQueryCache ] = useState(initQuery)
  const currentQuery = useRef(initQuery)

可以看到这里设置了三类 query, quryCache 用户重置参数, query 用于更新视图, currentQuery 用于获取最新参数. 这样设置的原因需要结合请求及参数的更新来看

  • 分页数更新
  
       // 请求数据
       const [ res, err ] = await asyncVessel(requery(currentQuery.current))

        if(err) return

        updateQuery(currentQuery.current)  // 更新 query
        
        setEnd( !res.result || !res.result.length)
        setList( prev => [ ...prev, ...res.result ]  )

这里会在请求完成后更新查询参数,主要为了统一分页数,为什么要在请求完成后更新分页数呢?请求新的分页数据前,分页数都是需要自增的, 设想如果我们在请求前更新分页数,而此时请求失败。用户再次请求数据时,将跳过前一次失败的数据。

  • 参数修改


    QQ截图20200410191441.png

通常参数的修改也是用户交互的一部分, 简单的通过表单或开关修改, 这是我们需要将参数与组件绑定在一起,但这时就会遇到一个问题。 修改参数后如何更新列表

   // setQuery 更新数据是一个异步的过程,通过设置参数后直接调用load,并不可靠
  const [  query,  setQuery ] = useState({....}) 
  loadList(query)  // 这样只能拿到旧的query值
// 通过 useEffect

useEffect(
 () => {
     loadList( query )
 },
[ query.sort ]
)
//  这样可以获取到 query 最新的值,但丢失了主动触发请求的能力。 
// 并不是每次查询数据的更新都需要列表数据。
// 如果使用中间变量做缓存,那内置query state 就没有多大意义了。
  • useRef 与 useState 的区别
// useState
 const [ query, setQuery ] = useState(initQuery)  // 每次更新 返回新的 query, setQuery

// useRef
const currentQuery = useRef(initQuery)  // 始终返回同一对象

除了返回值不同外,主要的区别有两点

  1. useState 每次更新都返回新的值, useRef 始终指向同一对象。
  2. useState 的值更新将触发视图更新, useRef 不会触发关联视图的更新。
// 封装参数更新函数
 const updateQuery = useCallback(
        data => {
            currentQuery.current = { ...currentQuery.current, ...data }
            setQuery( prev => ({...prev, ...data}) )
        },

        [ query, setQuery ]
    )

正式因为前面的特点,所以独立封装了参数的更新函数,同时更新currentQuery, query 保证取值及视图展示。

问题2 依赖

react hook 与 vue hook 明显的区别之一,react 需要我们手动关联并处理依赖,保证取值的正确及效率.

  
   //  使用useCallback 只在关联依赖更新时,更新函数。
    const reloadList = useCallback( async () => {
        
        setEnd(false)

        updateQuery({ pageNo: 1 })
        setList([])

        const [ res, err ] = await asyncVessel(requery(currentQuery.current))

        if(err) return

        setList( [...res.result] )
        
    }, [ isEnd, setEnd, setList, requery ])  // setState 函数也在依赖范围内


相关文章

  • useList 列表hook

    列表是我们日常开发中经常会碰到的一类展示形式, 只是以不同的 UI 显示在用户面前,例如: 菜单,表格等,其中一些...

  • useList vue 列表hook

    useList 列表操作hook, 对useSet 的二次封装 Example Params 名称说明默认值in...

  • Vue 3 响应性机制 之 watch 应用

    直接简单粗暴地上实例,这是一个用来获取分页列表数据的组合函数: ?src/composables/useList....

  • 微信技术协议

    微信协议 企业微信hook逆向 微信接口 pc企业微信hook接口,企业微信营销软件,企业微信群发 功能列表: 企...

  • useVirtualList

    简介 1 .虚拟化列表能力的hook,用于展示海量数据渲染时首屏渲染缓慢和卡顿的问题 扩展 1 .和无限列表相结合...

  • Small插件源码分析

    导读 目标读者使用方式机制原理关键概念关键流程涉及文件Hook点列表Small源码解析参考Android源码参考 ...

  • 堆利用的手法:

    malloc_hook realloc_hook+free_hook free_hook unsorted_bin...

  • hook原理小结

    常用的hook方式主要有导入表hook、导出表hook和inline hook三种。 一,导入表hook 首先需要...

  • Xposed Hook 魔趣列表动画 xuimod

    当年感觉魔趣的列表动画挺有意思,后来发现xuimod[https://github.com/zst123/XuiM...

  • 常用油猴Hook插件

    hook cookie hook 过debugger

网友评论

      本文标题:useList 列表hook

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