最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React系列useSyncExternalStore學(xué)習(xí)詳解

 更新時間:2022年07月19日 14:50:59   作者:0o華仔o0  
這篇文章主要為大家介紹了React系列useSyncExternalStore的學(xué)習(xí)及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

Concurrent 模式是 React18 中最引人矚目的新特性。通過使用 useTransition、useDeferredValue,更新對應(yīng)的 reconcile 過程變?yōu)榭芍袛啵辉贂驗殚L時間占用主線程而阻塞渲染進程,使得頁面的交互過程可以更加流暢。

不過這種新特性,在給我們帶來用戶體驗提升的同時,也給我們帶來了新的挑戰(zhàn)。在我們使用諸如 redux、mobx 等第三方狀態(tài)庫時,如果開啟了 Concurrent 模式,那么就有可能會出現(xiàn)狀態(tài)不一致的情形,給用戶帶來困擾。針對這種情況, React18 提供了一個新的 hook - useSyncExternalStore,來幫助我們解決此類問題。

今天,我們就通過本文,給大家梳理一下狀態(tài)不一致的情形以及 useSyncExternalStore 的使用情形、內(nèi)部原理。

useSyncExternalStore 初體驗

首先說明,useSyncExternalStore 這個 hook 并不是給我們在日常項目中用的,它是給第三方類庫如 Redux、Mobx 等內(nèi)部使用的。

我們先來看一下官網(wǎng)是怎么介紹 useSyncExternalStore 的。

useSyncExternalStore is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. It removes the need for useEffect when implementing subscriptions to external data sources, and is recommended for any library that integrates with state external to React.

翻譯過來就是:useSyncExternalStore 是一個新的鉤子,它通過強制的同步狀態(tài)更新,使得外部 store 可以支持并發(fā)讀取。它實現(xiàn)了對外部數(shù)據(jù)源訂閱時不在需要 useEffect,并且推薦用于任何與 React 外部狀態(tài)集成的庫。

useSyncExternalStore 這個新的 hook 的用法如下:

const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot )

其中,subscribe 是 external store 提供的 subscribe 方法;getSnapshot、getServerSnapshot 是用于獲取指定 store 狀態(tài)的方法,類似于 react-redux 中使用 useSelector 時需要傳入的 selector,一個用于客戶端渲染,一個用于服務(wù)端渲染;返回值就是我們在組件中可用的 store 狀態(tài)。

看完上面的翻譯和 api 介紹,大家是不是有點一臉懵逼呢???,說實話,我一開始看到這個 hook,也不知道該怎么使用它,翻閱了不少資料之后才知道它的使用正確姿勢。接下來,我就結(jié)合自己的學(xué)習(xí)經(jīng)歷,通過幾個簡單的小 demo,為大家梳理一下 useSyncExternalStore 的用法以及原理:

  • Concurrent 模式下使用 react-redux-7 出現(xiàn)狀態(tài)不一致的情形;
  • Concurrent 模式下使用 react-redux-8 解決狀態(tài)不一致;
  • 在自定義 external store 中使用 useSyncExternalStore;

Concurrent 模式下使用 react-redux 7

當(dāng)我們在項目中使用 react-redux-7 版本時,如果開啟 Concurrent 模式,并且在 reconcile 過程中斷的時候修改 store,那么就有可能會出現(xiàn)狀態(tài)不一致的情形,示例如下: useSyncExternalStore-react-redux-7

在示例中,我們可以很清楚的看到 store 狀態(tài)不一致情形。

其中,在組件 TextBox 中,我們使用 startTransition 包裹 store 修改狀態(tài)的代碼, 使得更新采用 Concurrent 模式:

const TextBox = () => {
    const dispatch = useDispatch();
    const handleValueChange = (e) => {
        startTransition(() => {
            dispatch({ type: "change", value: e.target.value });
        });
   };
   return <input onChange={handleValueChange} />;
};

在組件 ShowText 中,我們通過一個 while loop,將組件的 reconcile 過程人為的調(diào)整為 > 5ms, 如下:

const ShowText = () => {
    const value = useSelector((state) => state);
    const start = performance.now();
    while (performance.now() - start < 20) {}
    return <div>{value}</div>;
};

打開 performance 面板,整個過程如下:

更新開始后,有 10 個 ShowText 節(jié)點需要 reconcile, 每個節(jié)點 reconcile 時需要耗時 20ms 以上,這就導(dǎo)致每個 ShowText reconcile 結(jié)束以后都需要中斷讓出主線程。在協(xié)調(diào)中斷時,修改 store 狀態(tài),后續(xù)的 ShowText 節(jié)點在恢復(fù) reconcile 時,會使用修改以后的 store 狀態(tài),導(dǎo)致最后出現(xiàn)狀態(tài)不一致的情況。

細(xì)心的同學(xué)可能會好奇上面為什么會出現(xiàn)兩次 reconcile,并且最后所有的 ShowText 組件都顯示同樣的 store 狀態(tài)。這是因為react 會為每一次更新分配一條 lane,每次 reconcile 只處理指定 lane 的更新。當(dāng)我們給 TextBox 做第一次 input 時,觸發(fā) react 更新, 分配 lane 為 64,然后開始 reconcile。在 reconcile 過程中,又做了兩次 input, 觸發(fā)兩次 react 更新, 分配的 lane 為 128、256。lane 為 64 的 reconcile 結(jié)束以后,開始處理 lane 為 384(128 + 256, 128 和 256 的優(yōu)先級一樣,一起處理) 的更新,處理時 store 狀態(tài)為 123, 所有所有的 ShowText 節(jié)點在第二次 reconcile 時顯示 123。(如果還不理解,可以在留言區(qū)留言,到時再給大家詳細(xì)講解)。

針對 Concurrent 模式下狀態(tài)會出現(xiàn)不一致的情形,react-redux 在最新發(fā)布的版本 8 中引入了 useSyncExternalStore,修復(fù)了這一問題。

Concurrent 模式下使用 react-redux 8

useSyncExternalStore-react-redux-8 中,我們使用了 react-redux 最新發(fā)布的 8.0.0 版本,效果如下:

在示例中,我們發(fā)現(xiàn)修改 store 狀態(tài)時,不再出現(xiàn)狀態(tài)不一致的情形。但是很明顯,TextBox 的交互出現(xiàn)了卡頓,不再像 useSyncExternalStore-react-redux-7 中那樣的流暢。

這是為什么呢?難道沒有觸發(fā) Concurrent 模式嗎?

打開 performance 面板,整個過程如下:

通過上圖,我們可以發(fā)現(xiàn) reconcile 過程變?yōu)椴豢芍袛嗟?。由?reconcile 過程不可中斷,那么 ShowText 節(jié)點顯示的狀態(tài)當(dāng)然就一致了。

通過這個示例,我們可以看到 useSyncExternalStore 解決狀態(tài)不一致的方式就是將 reconcile 過程變?yōu)椴豢芍袛唷?這一點,你有想到嗎??? )。

那 react-redux-8 中是如何使用 useSyncExternalStore 的呢 ?

考慮到 react-redux 的源碼實現(xiàn)還是挺復(fù)雜的,我們這里通將過一個簡單的自定義 external store 來為大家展示 useSyncExternalStore 的用法,方便大家更好的理解這個新的 hook 該怎么樣使用。

Concurrent 模式下使用自定義 external store

首先,我們來定義一個非常簡單的 external store。類比 redux 和 react-redux,這個簡單的 external store 也會提供類似 createStore、useSelector、useDispatch 的功能。

整個 external store 的核心代碼如下:

    const { useState, useEffect } from 'react';
    const createStore = (initialState) => {
        let state = initialState;
        const getState = () => state;
        const listeners = new Set();
        // 通過 useDispatch 返回的 dispatch 修改 state 時,會觸發(fā) react 更新
        const useDispatch = () => {
            return (newState) => {
                state = { ...state, ...newState }
                listeners.forEach(l => l());
            }
        };
        // 訂閱 state 變化
        const subscribe = (listener) => {
            listeners.add(listener);
            return () => {
                listeners.delete(listener)
            };
        }
        return { getState, useDispatch, subscribe }
   }
   const useSelector = (store, selector) => {
       const [state, setState] = useState(() => selector(store.getState()));
       useEffect(() => {
           const callback = () => setState(selector(store.getState()));
           const unsubscribe = store.subscribe(callback);
           return unsubscribe;
        }, [store, selector]);
        return selector(store.getState());
   }

在這個 external store 中,我們可以通過 useSelector 獲取需要的公共狀態(tài),然后通過 useDispatch 返回的 dispatch 去修改公共狀態(tài),并觸發(fā) react 更新。

在這里,我們是基于發(fā)布訂閱模式來實現(xiàn)修改公共狀態(tài)來觸發(fā) react 更新。使用 useSelector 時,注冊 callback;使用 dispatch 時,修改公共狀態(tài),遍歷并執(zhí)行注冊的 callback,通過執(zhí)行 useState 返回的 setState 觸發(fā) react 更新。

基于這個自定義 external store,我們來實現(xiàn)如 useSyncExternalStore-react-redux-7 效果,示例 external-store 如下:

觀察示例,我們可以清楚的看到狀態(tài)不一致的情形。

針對這種情形,我們可以使用 useSyncExternalStore 來改造 useSelector,過程如下:

import { useSyncExternalStore } from 'react';
const useSelectorByUseSyncExternalStore = (store, selector) => {
    return useSyncExternalStore(
        store.subscribe, 
        useCallback(() => selector(store.getState()), [store, selector])
    );
}

external-store-useSyncExternalStore 中,我們使用了改造以后的 useSelectorByUseSyncExternalStore,效果如下:

狀態(tài)不一致問題解決,???,不過同樣的 Concurrent 模式也取消了,??。

實際上,react-redux-8 中使用 useSyncExternalStore 的方式和我們的自定義 external store 基本類似,在這里就不過多的展開了,感興趣的同學(xué)可以自己去看看 useSelector 的實現(xiàn)過程。

到這里,本節(jié)的內(nèi)容就結(jié)束了,相信通過上面的幾個 demo,大家能對狀態(tài)不一致以及 useSyncExternalStore 的用法有了初步的認(rèn)識了吧。了解了用法之后,我們接下來就來看看為什么 useSyncExternalStore 可以解決狀態(tài)不一致的問題。

源碼分析

其實,在上一節(jié) useSyncExternalStore-react-redux-8 中,我們通過 performance 分析面板,就已經(jīng)可以知道 useSyncExternalStore 解決狀態(tài)不一致的方式就是將 reconcile 過程從 Concurrent 模式變?yōu)?Sync 模式即同步不可中斷。

useSyncExternalStore

關(guān)于這一點,我們可以看看 useSyncExternalStore 相關(guān)源碼,看看它是怎么實現(xiàn)的。

首先是 useSyncExternalStore 在 mount 階段時要執(zhí)行的 mountSyncExternalStore 方法。

// 掛載階段,執(zhí)行 mountSyncExternalStore
function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
     // 當(dāng)前正在處理的 fiber node
    var fiber = currentlyRenderingFiber$1;
    // 掛載階段,生成 hook 對象
    var hook = mountWorkInProgressHook();
    // store 的快照
    var nextSnapshot;
    // 判斷當(dāng)前協(xié)調(diào)是否是 hydrate
    var isHydrating = getIsHydrating();
    if (isHydrating) {
        // hydrate, 先不用考慮
        ...
        nextSnapshot = getServerSnapshot();
        ...
    } else {
        // 獲取到的新的 store 的值
        nextSnapshot = getSnapshot();
        ...
        var root = getWorkInProgressRoot();
        ...
        if (!includesBlockingLane(root, renderLanes)) {
            // 一致性檢查, concurrent 模式下需要進行一致性檢查
            pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
        }
    }
    // hook 對象存儲 store 的快照
    hook.memoizedState = nextSnapshot;
    var inst = {
      value: nextSnapshot,
      getSnapshot: getSnapshot
    };
    hook.queue = inst; 
    // 相當(dāng)于 mount 階段執(zhí)行 useEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]);
    mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]);
    ...
    // 標(biāo)記 Passive$1 副作用,需要在 commit 階段進行一致性檢查,判斷store 是否發(fā)生變化
    pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null);
    return nextSnapshot;
}

在 mountSyncExternalStore 中,主要做了四件事情:

  • 執(zhí)行 getSnapshot 方法獲取當(dāng)前 store 狀態(tài)值,并存儲在 hook 中;
  • consistency check - 一致性檢查設(shè)置,在 render 階段結(jié)束時要進行 store 的一致性檢查;
  • 利用 mountEffect,即 useEffect 在 mount 階段執(zhí)行的方法,在節(jié)點 mount 完成以后執(zhí)行 store 對外提供的 subscribe 方法進行訂閱;
  • 標(biāo)記 Passive$1 副作用,在 commit 階段再進行一次 consistency check;

我們再來看一下 subscribeToStore、pushStoreConsistencyCheck、updateStoreInstance 的實現(xiàn):

subscribeToStore

function subscribeToStore(fiber, inst, subscribe) {
    // handleStoreChange 方法在我們通過 store 的 dispatch 方法修改 store 時會觸發(fā)
    var handleStoreChange = function () {
      if (checkIfSnapshotChanged(inst)) {
        // 如果 store 發(fā)生變化,采用阻塞模式渲染
        forceStoreRerender(fiber);
      }
    }; 
    // 使用 store 提供的 subscribe 方法去訂閱
    return subscribe(handleStoreChange);
}
// 用于判斷 store 是否發(fā)生變化
function checkIfSnapshotChanged(inst) {
    var latestGetSnapshot = inst.getSnapshot;
    // 之前的 store 值
    var prevValue = inst.value;
    try {
      // 新的 store 值
      var nextValue = latestGetSnapshot();
      // 淺比較 prevValue, nextValue
      return !objectIs(prevValue, nextValue);
    } catch (error) {
      return true;
    }
}
// 使用同步阻塞模式渲染
function forceStoreRerender(fiber) {
    scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp);
}

subscribeToStore 中通過 store 提供的 subscribe 方法訂閱了 store 狀態(tài)變化。當(dāng)我們通過 store 提供的 dispatch 方法修改 store 時,store 會遍歷依賴列表,按序執(zhí)行訂閱的 callback。此時 handleStoreChange 方法執(zhí)行,由于 store 狀態(tài)發(fā)生了變化,執(zhí)行 forceStoreRerender 方法, 手動觸發(fā) Sync 阻塞渲染。

pushStoreConsistencyCheck

  // 一致性檢查配置,如果是 concurrent 模式,會構(gòu)建一個 check 對象添加到 fiber node 的 updateQueue 對象的 store 數(shù)組中
  function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
    fiber.flags |= StoreConsistency;
    var check = {
      getSnapshot: getSnapshot,
      value: renderedSnapshot
    };
    var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
    if (componentUpdateQueue === null) {
      componentUpdateQueue = createFunctionComponentUpdateQueue();
      currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
      // 收集 check 對象
      componentUpdateQueue.stores = [check];
    } else {
      var stores = componentUpdateQueue.stores;
      // 收集 check 對象
      if (stores === null) {
        componentUpdateQueue.stores = [check];
      } else {
        stores.push(check);
      }
    }
 }
 // iber tree 的整個協(xié)調(diào)過程
 function performConcurrentWorkOnRoot(root, didTimeout) {
    ...
    // 判斷采用 concurrent or sync 模式
    var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && ( !didTimeout);
    var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);
    if (exitStatus !== RootInProgress) { // 協(xié)調(diào)結(jié)束
      if (exitStatus === RootErrored) {
        // 出現(xiàn)異常
        ...
      }
      if (exitStatus === RootFatalErrored) {
        // 出現(xiàn)異常
        ...
      }
      if (exitStatus === RootDidNotComplete) {
        // suspense 掛起
        ...
      } else {
        // 協(xié)調(diào)完成
        var renderWasConcurrent = !includesBlockingLane(root, lanes);
        var finishedWork = root.current.alternate;
        // 如果是 concurrent 模式,需要進行 store 的一致性檢查
        if (renderWasConcurrent && !isRenderConsistentWithExternalStores(finishedWork)) {
          // store 狀態(tài)不一致,采用同步阻塞渲染
          exitStatus = renderRootSync(root, lanes);
          ...
        }
      ...
      finishConcurrentRender(root, exitStatus, lanes);
    }
    ...
  }

為了保證 store 的狀態(tài)一致,react 在 mountSyncExternalStore 方法中,先通過 pushStoreConsistencyCheck 給組件節(jié)點配置 check 對象,然后在協(xié)調(diào)完成以后,再遍歷一次 fiber tree,基于節(jié)點的 check 對象做狀態(tài)一致性檢查。如果發(fā)現(xiàn) store 狀態(tài)不一致,那么就通過 renderRootSync 方法重新進行一次 Sync 阻塞渲染。

updateStoreInstance

function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
    inst.value = nextSnapshot;
    inst.getSnapshot = getSnapshot; 
    if (checkIfSnapshotChanged(inst)) {
      // 在 commit 階段,檢查 store 是否發(fā)生變化,如果發(fā)生變化,觸發(fā)同步阻塞渲染
      forceStoreRerender(fiber);
    }
}

在 commit 階段,需要處理 render 階段收集的 effect。此時,如果發(fā)現(xiàn) store 發(fā)生變化,那么在瀏覽器渲染之前,還要重新進行一次 Sync 阻塞渲染,以保證 store 狀態(tài)一致。

看完 mountSyncExternalStore 的實現(xiàn)之后,我們再來看一下 useSyncExternalStore 在 update 階段要執(zhí)行的 updateSyncExternalStore 的實現(xiàn)。

function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
    var fiber = currentlyRenderingFiber$1;
    // 獲取 hooke 對象
    var hook = updateWorkInProgressHook(); 
    // 獲取新的 store 狀態(tài)
    var nextSnapshot = getSnapshot();
    ...
    var inst = hook.queue;
    updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); 
    if (inst.getSnapshot !== getSnapshot || snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
    // checking whether we scheduled a subscription effect above.
    workInProgressHook !== null && workInProgressHook.memoizedState.tag & HasEffect) {
      fiber.flags |= Passive;
      pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null); 
      var root = getWorkInProgressRoot();
      ...
      if (!includesBlockingLane(root, renderLanes)) {
        // 一致性檢查配置
        pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
      }
    }
    return nextSnapshot;
  }

updateSyncExternalStore 和 mountSyncExternalStore 做的事情差不多,主要做了:

  • 執(zhí)行 getSnapshot 方法獲取當(dāng)前 store 狀態(tài)值,并存儲在 hook 中;
  • 利用 updateEffect,即 useEffect 在 update 階段執(zhí)行的方法,在節(jié)點更新完成以后執(zhí)行 store 對外提供的 subscribe 方法(如果 store 提供的 subscribe 方法沒有發(fā)生變化,這一步不會執(zhí)行);
  • 標(biāo)記 Passive$1 副作用,在 commit 階段進行一致性檢查;
  • consistency check - 一致性檢查設(shè)置,在 render 階段結(jié)束時要進行 store 的一致性檢查;

通過上面的源碼分析,我們可以了解到 useSyncExternalStore 保證 store 狀態(tài)一致的手段就是協(xié)調(diào)采用 Sync 不可中斷渲染。

為了達(dá)到這個目的,useSyncExternalStore 采用了三道保險:

  • 通過 dispatch 修改 store 狀態(tài)時,強制使用 Sync 同步不可中斷渲染;
  • Concurrent 模式下,協(xié)調(diào)結(jié)束以后會進行一致性檢查,如果發(fā)現(xiàn)狀態(tài)不一致,強制重新進行一次 Sync 同步不可中斷渲染;
  • commit 階段時,再進行一次一致性檢查,如果發(fā)現(xiàn)狀態(tài)不一致,強制重新進行一次 Sync 同步不可中斷渲染;

寫在最后

到這里,關(guān)于 useSyncExternalStore 的介紹就結(jié)束了。相信通過本文,大家能對 useSyncExternalStore 這個新的 hook 有一定的了解了吧。如果大家覺得本文還不錯,那就給我點個贊吧,??。

參考資料

React 18 CHANGELOG

以上就是React系列useSyncExternalStore學(xué)習(xí)詳解的詳細(xì)內(nèi)容,更多關(guān)于React useSyncExternalStore的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 深入理解React Native核心原理(React Native的橋接(Bridge)

    深入理解React Native核心原理(React Native的橋接(Bridge)

    這篇文章主要介紹了深入理解React Native核心原理(React Native的橋接(Bridge),本文重點給大家介紹React Native的基礎(chǔ)知識及實現(xiàn)原理,需要的朋友可以參考下
    2021-04-04
  • react優(yōu)雅處理多條件鼠標(biāo)拖拽位移

    react優(yōu)雅處理多條件鼠標(biāo)拖拽位移

    這篇文章主要為大家詳細(xì)介紹了react優(yōu)雅處理多條件鼠標(biāo)拖拽位移,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • react中監(jiān)聽props的改變方式

    react中監(jiān)聽props的改變方式

    這篇文章主要介紹了react中監(jiān)聽props的改變方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • react hooks實現(xiàn)防抖節(jié)流的方法小結(jié)

    react hooks實現(xiàn)防抖節(jié)流的方法小結(jié)

    這篇文章主要介紹了react hooks實現(xiàn)防抖節(jié)流的幾種方法,文中通過代碼示例給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • React Native中WebView與html雙向通信遇到的坑

    React Native中WebView與html雙向通信遇到的坑

    這篇文章主要介紹了React Native中WebView與html雙向通信的一些問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-01-01
  • React使用redux基礎(chǔ)操作詳解

    React使用redux基礎(chǔ)操作詳解

    這篇文章主要介紹了如何在React中直接使用Redux,目前redux在react中使用是最多的,所以我們需要將之前編寫的redux代碼,融入到react當(dāng)中去,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2023-01-01
  • react路由基礎(chǔ)解讀(Router、Link和Route)

    react路由基礎(chǔ)解讀(Router、Link和Route)

    這篇文章主要介紹了react路由基礎(chǔ)解讀(Router、Link和Route),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Vite+React+TypeScript手?jǐn)]TodoList的項目實踐

    Vite+React+TypeScript手?jǐn)]TodoList的項目實踐

    本文主要介紹了Vite+React+TypeScript手?jǐn)]TodoList的項目實踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • React-Native中props具體使用詳解

    React-Native中props具體使用詳解

    本篇文章主要介紹了React-Native中props具體使用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • React合成事件原理及實現(xiàn)(React18和React16)

    React合成事件原理及實現(xiàn)(React18和React16)

    本文主要介紹了React合成事件原理及實現(xiàn),包含React18和React16兩種版本,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02

最新評論

蓬安县| 宿迁市| 嘉善县| 清镇市| 道孚县| 海丰县| 邹平县| 霍城县| 沛县| 筠连县| 积石山| 句容市| 开平市| 白朗县| 耿马| 哈尔滨市| 涪陵区| 南江县| 革吉县| 浠水县| 沙坪坝区| 东山县| 济阳县| 新野县| 昭平县| 老河口市| 福泉市| 邛崃市| 邯郸市| 东丽区| 澎湖县| 亚东县| 邵武市| 叙永县| 稷山县| 清涧县| 平南县| 靖江市| 阳新县| 邳州市| 平利县|