今天写一个hook,正想发挥hooks这种高级复用方式来缩短我的开发时间,就出现了一个新bug。
我编写的这个hook用于管理数据列表状态。除了导出内部的状态外,还导出一些方法供外部调用。代码简化如下:
function useDataList() { const [rows, setRows] = useState([]) const [pageIndex, setPageIndex] = useState(0) async function loadNextPage() { const res = await api.searchData(pageIndex+1) setRows(res.data.rows) setPageIndex(index+1) } return { rows, setPageIndex, nextPage } }
然后这样使用这个hook:
function App() { const {rows, setPageIndex, loadNextPage} = useDataList() useEffect(()=>{ setPageIndex(-1) // 因为loadNextPage中会给pageIndex加一,而初始我们希望请求第0页,因此设为-1 // 因为setPageIndex()不会立刻改变pageIndex,因此要在下一个事件循环调用loadNextPage() setTimeout(()=>{ loadNextPage() }) }, []) return ( {/* 这里使用rows渲染列表 */} ) }
好,现在问题出现了。api.searchData()请求的是第1页,而不是第0页,你知道为什么吗?
原因就在于产生了闭包。
useEffect在App第一次渲染的时候执行,以后不再执行。
这时loadNextPage指向的是第一次App()指向时的loadNextPage,
而这个loadNextPage是第一次执行useDataList时导出的,
它内部的pageIndex保存的是第一次执行useDataList()时的pageIndex的值,也就是0。
因此调用loadNextPage()时,请求的页码是pageIndex+1=0+1=1。
我这种情况和网上说的hooks陷阱有点不一样,但是原理是一样的,都是闭包问题。
查阅资料发现,可以使用ahooks的useMemorizedFn解决这个问题。
这个API可以保持传入的函数不变,但是每次函数执行时访问的都是最新的state。
于是代码这样改:
function App() { const {rows, setPageIndex, loadNextPage} = useDataList() const wrapedLoadNextPage = useMemorized(()=>{ loadNextPage() }) useEffect(()=>{ setPageIndex(-1) setTimeout(()=>{ wrapedLoadNextPage() }) }, []) return ( {/* 这里使用rows渲染列表 */} ) }
最后感叹一下,原本以为新技术哪有什么难的,只要迁移以前的知识就行了呗。
结果发现,同一个功能点,用不同的技术实现就是有差别,新的技术产生新的问题,从而导致项目延期。
比如会使用vue开发网页,使用rn开发APP问题应该不大,结果rn的语法和vue不同。
又比如使用uniapp开发过小程序,那使用rn开发APP应该会挺快,结果RN的生态真的简陋,没有uniapp那么齐全方便。
再比如hooks解决了class组件复用上的一些问题,那用起来应该很顺手,结果出了今天的hooks陷阱的问题。
...
下一次学习新技术,要谨慎。
标签:pageIndex,一次,const,setPageIndex,hooks,loadNextPage,rows,陷阱 From: https://www.cnblogs.com/hdxg/p/17135909.html