<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          use + Suspense 新思路極簡代碼實現(xiàn)數(shù)據(jù)加載更多

          共 12121字,需瀏覽 25分鐘

           ·

          2024-06-18 09:00

          React 19 的開發(fā)體驗實在是太好了!

          自從徹底掌握了 React 19 之后,我感覺自己更愛寫 React 代碼了。比如,像分頁列表這種復(fù)雜交互,核心邏輯只需要簡單幾行代碼就可以搞定。

          分頁列表是我們?nèi)粘i_發(fā)中,比較常見的需求。其中,通過點擊或者滾動來觸發(fā)加載更多是主流的交互方式之一。

          這篇文章要帶大家實現(xiàn)的效果如下圖所示。

          為了便于大家更容易理解和消化,我們先通過一個更簡單的案例來理解代碼思路,然后再實現(xiàn)最終目標(biāo)。

          • 傳統(tǒng)方式實現(xiàn)請求結(jié)果新增到列表中
          • react19 中實現(xiàn)新增列表內(nèi)容
          • react19 中通過點擊按鈕實現(xiàn)分頁列表加載更多

          0

          傳統(tǒng)方案實現(xiàn)請求結(jié)果新增到列表

          首先,先定義請求數(shù)據(jù)的 promise

          // api.js
          export const getMessage = async () => {
            const res = await fetch('https://api.chucknorris.io/jokes/random')
            return res.json()
          }

          然后需要定義一個狀態(tài)用于存儲列表。

          const [list, updateList] = useState([])

          由于每一項在請求時,都需要顯示一個 Loading 狀態(tài),此時我們可以使用一個巧妙的方式來解決這個問題。那就是暫時往 list 中新增一條 type: loading 的數(shù)據(jù)。在遍歷的時候判斷出該數(shù)據(jù)渲染成 Skeleton 組件。

          因此,我們單獨聲明一個列表組件 List,該組件接收 list 作為參數(shù)

          function List(props{
            const list = props.list
            return (
              <>
                {list.map((item, index) => {
                  if (item.type === 'loading') {
                    return <Skeleton />
                  }
                  return <Userinfo index={index} username={item.id} message={item.value} />
                })}
              </>

            )
          }

          當(dāng)我們在發(fā)送請求時,先往 list 中新增一條 type: loading 的數(shù)據(jù)。此時我們利用 list 的特性與閉包的緩存特性,在接口請求成功之后再把請求過來的有效數(shù)據(jù)更新到 list 中即可。

          代碼如下

          useEffect(() => {
            updateList([...list, {type'loading'}])
            getMessage().then(res => {
              updateList([...list, res])
            })
          }, []);

          完整代碼如下:

          import {use, useState, Suspense, useEffect} from 'react'
          import Userinfo from './Userinfo'
          import Skeleton from './Skeleton'
          import Button from './Button'
          import {getMessage} from './api'

          export default function Demo01({
            const [list, updateList] = useState([])

            useEffect(() => {
              updateList([...list, {type'loading'}])
              getMessage().then(res => {
                updateList([...list, res])
              })
            }, []);

            function __handler({
              updateList([...list, {type'loading'}])
              getMessage().then(res => {
                updateList([...list, res])
              })
            }

            return (
              <>
                <div className='text-right mb-4'>
                  <Button onClick={__handler}>新增數(shù)據(jù)</Button>
                </div>
                <List list={list} />
              </>

            )
          }

          function List(props{
            const list = props.list
            return (
              <>
                {list.map((item, index) => {
                  if (item.type === 'loading') {
                    return <Skeleton />
                  }
                  return <Userinfo index={index} username={item.id} message={item.value} />
                })}
              </>

            )
          }

          1

          新的思路

          舊的思路在實現(xiàn)上非常巧妙。但是簡潔度依然弱于新的實現(xiàn)方案。除此之外,舊的實現(xiàn)思路還有許多問題需要處理,例如初始化時請求了兩次,我們要考慮接口防重的問題。以及當(dāng)我們多次連續(xù)點擊按鈕時,會出現(xiàn)競態(tài)問題而導(dǎo)致渲染結(jié)果出現(xiàn)混亂。

          我們基于 use + Suspense 的思路來考慮新的方案。

          首先,我們應(yīng)該將數(shù)據(jù)存儲在 promise 中,因此很自然就能想到,多個數(shù)據(jù),那么我們應(yīng)該需要維護(hù)多個 promise,因此,我們需要定義一個由 promise 組成的數(shù)組。

          const [promise, updatePromise] = useState(() => [getMessage()])

          由于初始化時,我們需要自動請求一條數(shù)據(jù),因此我們給該數(shù)組的初始值為 [getMessage()]

          點擊時,需要新增一個數(shù)據(jù),那么其實就是新增一個 promise,所以代碼也非常簡單,就是如下所示

          function __handler({
            updatePromise([...promise, getMessage()])
          }

          處理好之后,我們只需要使用 map 遍歷該數(shù)組即可。在遍歷邏輯中,每一項都返回 Suspense 包裹的子組件。我們將 promise 傳遞給該子組件,并在子組件中使用 use 讀取 promise 中的值。

          最終的代碼實現(xiàn)如下。

          export default function Demo01({
            const [promise, updatePromise] = useState(() => [getMessage()])

            function __handler({
              updatePromise([...promise, getMessage()])
            }

            return (
              <>
                <div className='text-right mb-4'>
                  <Button onClick={__handler}>新增數(shù)據(jù)</Button>
                </div>
                {promise.map((item, index) => (
                  <Suspense fallback={<Skeleton />} key={`hello ${index}`}>
                    <User promise={item} index={index} />
                  </Suspense>
                ))}
              </>

            )
          }

          function User(props{
            const result = use(props.promise)
            return (
              <Userinfo index={props.index} username={result.id} message={result.value} />
            )
          }

          此時通過案例演示結(jié)果可以觀察到,初始化時的接口重復(fù)問題被解決掉了,并且當(dāng)我們多次連續(xù)點擊新增時,也不會出現(xiàn)接口競態(tài)混亂的問題。

          希望大家能夠通過這個案例,進(jìn)一步感受到新的開發(fā)思維的強(qiáng)大之處。

          2

          點擊按鈕實現(xiàn)分頁列表加載更多

          我們可以在思維上將上一節(jié)的解決方案擴(kuò)展到分頁列表中,加載更多的場景。

          這里唯一的一個小區(qū)別就是,上一章中,我們只在 promise 中存儲了一條數(shù)據(jù)。如果我們將一頁數(shù)據(jù)也存在 promise 中呢?

          加載更多的分頁邏輯就會變得非常簡單。為了方便演示,我們這里以一頁數(shù)據(jù)只有三條為例。

          首先簡單約定接口,該接口返回一頁數(shù)據(jù)。3條

          // api.js
          const count = 3;
          const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`;

          export const fetchList = async () => {
            const res = await fetch(fakeDataUrl)
            return res.json()
          }

          然后定義一個可以遍歷顯示一頁數(shù)據(jù)的組件。該組件接收一個 promise,并使用 use 讀取請求結(jié)果。

          // List.jsx
          import { use } from 'react';
          export default function CurrentList({promise}{
            const {results} = use(promise)
            return (
              <div>
                {results.map((item, i) => (
                  <div key={item.name.last} className='flex border-b py-4 mx-4 items-center'>
                    <div className='flex-1'>
                      <div className='flex'>
                        <img className='w-14 h-14 rounded-full' src={item.picture.large} alt='' />
                        <div className='flex-1 ml-4'>
                          <div className='font-bold'>{item.name.last}</div>
                          <div className='text-gray-400 mt-3 text-sm line-clamp-1'>react 19 re, a design language for background applications</div>
                        </div>
                      </div>

                      <div className='mt-4 line-clamp-2 text-sm'>We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.</div>
                    </div>

                    <img
                      className='w-52 ml-2'
                      alt="logo"
                      src="https://qinglite-1253448069.cos.ap-shanghai.myqcloud.com/web/96b2b85f4c53744407dbed03982cf81c0e1dc322"
                    />

                  </div>
                ))}
              </div>

            )
          }

          此時我們稍微梳理一下邏輯,首先我們有多個 promise,然后每個 promise 中有一頁數(shù)據(jù),因此,我們可以遍歷 promise,并在遍歷中渲染能顯示一頁數(shù)據(jù)的 List 組件。

          因此,我們首先要定義一個狀態(tài)用于保存 promise 數(shù)組

          const [promises, increasePromise] = useState(() => [fetchList()])

          初始化時需要渲染一頁數(shù)據(jù),所以我們設(shè)置該數(shù)組的默認(rèn)值為 [fetchList()]

          loadmore 事件觸發(fā)之后,我們只需要往該數(shù)組中新增一個 promise 即可

          const onLoadMore = () => {
            increasePromise([...promises, fetchList()])
          };

          然后遍歷 promises,在遍歷中使用 Suspense 包裹內(nèi)部有 use 邏輯的 List 組件

          {promises.map((promise, i) => (
            <Suspense fallback={<Skeleton />} key={`hello ${i}`}>
              <List promise={promise} />
            </Suspense>

          ))}

          注意看,完整的代碼

          const Index = () => {
            const [promises, increasePromise] = useState(() => [fetchList()])

            const onLoadMore = () => {
              increasePromise([...promises, fetchList()])
            };

            return (
              <>
                {promises.map((promise, i) => (
                  <Suspense fallback={<Skeleton />} key={`hello ${i}`}>
                    <List promise={promise} />
                  </Suspense>
                ))}
                <div className='text-center my-4'>
                  <Button onClick={onLoadMore}>loading more</Button>
                </div>
              </>

            );
          };
          export default Index;

          非常 nice,我們用極簡的代碼實現(xiàn)了復(fù)雜的交互邏輯。

          i

          分頁參數(shù)的維護(hù)、最后一頁的判斷,大家在實踐中要自行維護(hù),這里只做方案的演示,沒有考慮所有邊界情況

          3

          合集介紹

          本文內(nèi)容與案例來自于我傾力打造的付費小冊 《React 19》。這本小冊將會是市面上學(xué)習(xí)體驗最好質(zhì)量最高的小冊,沒有之一。

          在這本小冊的文章中,所有的案例,都不再是以截圖的形式展示,而是以可操作,可交互的真實組件渲染而成。你可以輕松感受案例的最終形態(tài)。掃清學(xué)習(xí)過程中的認(rèn)知差異。

          除此之外,最終的完整代碼,與最佳實踐的案例演示,都會呈現(xiàn)在右側(cè)區(qū)域。你還可以通過修改代碼實時查看不同邏輯下的運(yùn)行結(jié)果,學(xué)習(xí)效果直接翻倍。

          并且每一個案例,我都精心設(shè)計了 UI 與 Loading 效果。確保案例也有最好的學(xué)習(xí)體驗。而不是簡單粗糙的案例。

          小冊內(nèi)容會包含大量實戰(zhàn)案例,確保每一位學(xué)完《React 19》的小伙伴都能所學(xué)即所得,并且在必要的案例中,我還會詳細(xì)對比新舊方案的差異。目前該小冊內(nèi)容已經(jīng)完成了一大半。

          瀏覽 84
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特级WWW444至码 | 中国熟妇乱伦 | 国产avwww | 亚洲无码中文字幕在线观看视频 | 男人天堂无码视频 |