01- react+hooks實現(xiàn)滾動加載之坑

本人剛剛入職新公司,以前都是寫Vue的螺捐,現(xiàn)在新公司技術棧使用的是react颠悬。一頓惡補后在實際的項目中還是避免不了踩坑,花大量的時間找原因定血,debug赔癌。。澜沟。不知所措的想哭QAQ
公司項目中需要實現(xiàn)一個滾動分頁加載數(shù)據(jù)得效果灾票,按照咱們邏輯應該是這樣的:
1.請求前先判斷l(xiāng)oading是否為true, 為true時return掉阻止請求函數(shù)調(diào)用,為false時將loading設置為true然后發(fā)起請求
2.請求完畢后再將loading設置為false,允許下次再發(fā)起請求

import React, { useEffect, useRef, useState } from 'react'
import styles from './index.module.scss'
import { LoadingOutlined } from '@ant-design/icons'; 
import {getScrollLoadList} from '../../api/scrollLoad'
function ScrollLoad() {
  const [list, setList] = useState<number[]>([])
  const [pageNum, setPageNum]=useState<number>(1)
  const [loading, setLoading] = useState<boolean>(false)
  const wrapRef = useRef<any>(null)
  useEffect(() => {
    const Dom = wrapRef.current
    Dom.addEventListener('scroll',loadMore)
    return () => {
      Dom.removeEventListener('scroll',loadMore)
    }
    // eslint-disable-next-line
  },[])
  useEffect(() => {
    getList()
    // eslint-disable-next-line
  },[pageNum])
  const getList = () => {
    setLoading(true)  // 設為請求狀態(tài)
    getScrollLoadList({pageNum}).then((res:any) => {
      const temp = res.result
      const nowList = pageNum === 1 ? temp : [...list,...temp]
      setList(nowList)
    }).finally(() => {
      setLoading(false)  // 請求完畢置為false
    })
  }
  const loadMore = (e:any) => {
    const {offsetHeight, scrollTop, scrollHeight} = e.target
    if(offsetHeight + scrollTop === scrollHeight) {
      if(loading) return    // 判斷是否在請求狀態(tài)
      setPageNum((pageNum)=> pageNum + 1)
    }
  }
  return (
    <div ref={wrapRef} className={styles.scroll_wrap}>
      {
        list && list.length && list.map(item => (
          <div key={item} className={styles.wrap_item}>{item}</div>
        ))
      }
      {loading && <div className={styles.loading}><LoadingOutlined /></div> }
    </div>
  )
}
export default ScrollLoad

咋一看好像代碼沒啥問題啊茫虽,但實際上問題大的去了刊苍。當連續(xù)快速的滾動時,這貨始終能調(diào)用接口濒析。loadingMore函數(shù)里的if(loading) return 并沒有產(chǎn)生什么卵用正什,并且打印出來始終為我們的初始值false,百思不得其解:判印Sさ!

那么為什么出現(xiàn)這種問題呢?經(jīng)過一番研究莹妒。是因為useEffect(() => {},[])這個副作用相當于class組件內(nèi)的生命周期componentDidMount,在組件渲染中只執(zhí)行一次名船。

重點來了

當上面代碼useEffect(() => {},[])執(zhí)行時會產(chǎn)生閉包,里面用到的state變量會進行緩存旨怠,只要這個閉包不被釋放渠驼,里面的state變量就不會是最新的值。即loading始終為初始狀態(tài)下的值false鉴腻。
那么怎么解決這個問題呢迷扇?
方案1:
可以將pageNum這個參數(shù)傳進去即useEffect(() => {},[loading]),當loading改變后爽哎,會銷毀之前的閉包蜓席,產(chǎn)生新的閉包,這樣就能保證里面使用的state變量是最新的,不過這種方法每次都得重新獲取dom元素课锌,設置監(jiān)聽和移除監(jiān)聽事件厨内,比較耗性能。(useEffect(() => {})也可以渺贤,但是不傳只要有狀態(tài)變化就會銷毀和新建相對來說更耗性能)
改動代碼如下:

useEffect(() => {
    const Dom = wrapRef.current
    Dom.addEventListener('scroll',loadMore)
    return () => {
      Dom.removeEventListener('scroll',loadMore)
    }
    // eslint-disable-next-line
  },[loading])         // 傳入loading,監(jiān)聽loading變化

方案2
通過設置一個局部變量雏胃。 可以在函數(shù)組件外定義一個變量或者函數(shù)內(nèi)使用useRef()創(chuàng)建一個變量(這里簡稱loadingRef),然后將state值loading賦值給這個變量,當loading改變時志鞍,會觸發(fā)loadingRef的改變瞭亮,這樣就會保證loadingRef是最新的值,然后通過loadingRef活loadingRef.current 去判斷即可

import React, { useEffect, useRef, useState } from 'react'
import styles from './index.module.scss'
import { LoadingOutlined } from '@ant-design/icons'; 
import {getScrollLoadList} from '../../api/scrollLoad'
// let loadingRef = false
function ScrollLoad() {
  const [list, setList] = useState<number[]>([])
  const [pageNum, setPageNum]=useState<number>(1)
  const [loading, setLoading] = useState<boolean>(false)
  const wrapRef = useRef<any>(null)
  const loadingRef = useRef<boolean>()
  loadingRef.current = loading
  // loadingRef = loading
  useEffect(() => {
    const Dom = wrapRef.current
      Dom.addEventListener('scroll',loadMore)
      console.log(Dom,loading)
    return () => {
      console.log('清空監(jiān)聽事件')
      Dom.removeEventListener('scroll',loadMore)
    }
    // eslint-disable-next-line
  },[])
  useEffect(() => {
    getList()
    // eslint-disable-next-line
  },[pageNum])
  const getList = () => {
    setLoading(true)
    getScrollLoadList({pageNum}).then((res:any) => {
      const temp = res.result
      const nowList = pageNum === 1 ? temp : [...list,...temp]
      setList(nowList)
    }).finally(() => {
      setLoading(false)
    })
  }
  const loadMore = (e:any) => {
    const {offsetHeight, scrollTop, scrollHeight} = e.target
    if(offsetHeight + scrollTop === scrollHeight) {
      console.log(loadingRef, '下拉加載之前')
      // if(loadingRef) return
      if(loadingRef.current) return
      setPageNum((pageNum)=> pageNum + 1)
    }
  }
  return (
    <div ref={wrapRef} className={styles.scroll_wrap}>
      {
        list && list.length && list.map(item => (
          <div key={item} className={styles.wrap_item}>{item}</div>
        ))
      }
      {loading && <div className={styles.loading}><LoadingOutlined /></div> }
    </div>
  )
}

export default ScrollLoad

案例代碼:https://github.com/lee-won/reactCaseDome

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末固棚,一起剝皮案震驚了整個濱河市统翩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌此洲,老刑警劉巖厂汗,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異黍翎,居然都是意外死亡面徽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門匣掸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趟紊,“玉大人,你說我怎么就攤上這事碰酝■伲” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵送爸,是天一觀的道長铛嘱。 經(jīng)常有香客問我暖释,道長,這世上最難降的妖魔是什么墨吓? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任球匕,我火速辦了婚禮,結果婚禮上帖烘,老公的妹妹穿的比我還像新娘亮曹。我一直安慰自己,他們只是感情好秘症,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布照卦。 她就那樣靜靜地躺著,像睡著了一般乡摹。 火紅的嫁衣襯著肌膚如雪役耕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天聪廉,我揣著相機與錄音瞬痘,去河邊找鬼。 笑死锄列,一個胖子當著我的面吹牛图云,可吹牛的內(nèi)容都是我干的惯悠。 我是一名探鬼主播邻邮,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼克婶!你這毒婦竟也來了筒严?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤情萤,失蹤者是張志新(化名)和其女友劉穎鸭蛙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筋岛,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡娶视,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睁宰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肪获。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柒傻,靈堂內(nèi)的尸體忽然破棺而出孝赫,到底是詐尸還是另有隱情,我是刑警寧澤红符,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布青柄,位于F島的核電站伐债,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏致开。R本人自食惡果不足惜峰锁,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望双戳。 院中可真熱鬧祖今,春花似錦、人聲如沸拣技。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膏斤。三九已至徐绑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間莫辨,已是汗流浹背傲茄。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沮榜,地道東北人盘榨。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像蟆融,于是被迫代替她去往敵國和親草巡。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 人類發(fā)展的進程中淘汰的永遠都是不思進取的守舊派型酥。React中亦是如此思想山憨,或許激進,但大多數(shù)人們總期待“新桃換舊符...
    DYBOY閱讀 2,401評論 0 1
  • Hook 是 React 16.8 的新增特性弥喉。它可以讓你在不編寫 class 的情況下使用 state 以及其他...
    mora__閱讀 587評論 0 0
  • 在開始之前郁竟,先看一張圖: 為什么要推出 React Hooks? React Hooks 的設計目的,就是加強版函...
    龐校長閱讀 1,576評論 1 4
  • react hooks demo 創(chuàng)建所需要的組件由境,這個項目我們分成三個組件棚亩, 分別是頭部,搜索框虏杰,電影列表// ...
    Joemoninni閱讀 697評論 0 3
  • 今天感恩節(jié)哎讥蟆,感謝一直在我身邊的親朋好友。感恩相遇嘹屯!感恩不離不棄攻询。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,566評論 0 11