欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React深入淺出分析Hooks源碼

 更新時(shí)間:2022年11月04日 12:01:00   作者:goClient1992  
在react類組件(class)寫法中,有setState和生命周期對(duì)狀態(tài)進(jìn)行管理,但是在函數(shù)組件中不存在這些,故引入hooks(版本:>=16.8),使開發(fā)者在非class的情況下使用更多react特性

useState 解析

useState 使用

通常我們這樣來使用 useState 方法

function App() {
  const [num, setNum] = useState(0);
  const add = () => {
    setNum(num + 1);
  };
  return (
    <div>
      <p>數(shù)字: {num}</p>
      <button onClick={add}> +1 </button>
    </div>
  );
}

useState 的使用過程,我們先模擬一個(gè)大概的函數(shù)

function useState(initialValue) {
  var value = initialValue
  function setState(newVal) {    
    value = newVal
  }
  return [value, setState]
}

這個(gè)代碼有一個(gè)問題,在執(zhí)行 useState 的時(shí)候每次都會(huì) var _val = initialValue,初始化數(shù)據(jù);

于是我們可以用閉包的形式來保存狀態(tài)。

const MyReact = (function() {
   // 定義一個(gè) value 保存在該模塊的全局中
  let value
  return {
    useState(initialValue) {
      value = value || initialValue 
      function setState(newVal) {
        value = newVal
      }
      return [value, setState]
    }
  }
})()

這樣在每次執(zhí)行的時(shí)候,就能夠通過閉包的形式 來保存 value。

不過這個(gè)還是不符合 react 中的 useState。因?yàn)樵趯?shí)際操作中會(huì)出現(xiàn)多次調(diào)用,如下。

function App() {
  const [name, setName] = useState('Kevin');
  const [age, setAge] = useState(0);
  const handleName = () => {
    setNum('Dom');
  };
  const handleAge = () => {
    setAge(age + 1);
  };
  return (
    <div>
      <p>姓名: {name}</p>
      <button onClick={handleName}> 改名字 </button>
       <p>年齡: {age}</p>
      <button onClick={handleAge}> 加一歲 </button>
    </div>
  );
}

因此我們需要在改變 useState 儲(chǔ)存狀態(tài)的方式

useState 模擬實(shí)現(xiàn)

const MyReact = (function() {
  // 開辟一個(gè)儲(chǔ)存 hooks 的空間
  let hooks = []; 
  // 指針從 0 開始
  let currentHook = 0 
  return {
    // 偽代碼 解釋重新渲染的時(shí)候 會(huì)初始化 currentHook
    render(Component) {
      const Comp = Component()
      Comp.render()
      currentHook = 0 // 重新渲染時(shí)候改變 hooks 指針
      return Comp
    },      
    useState(initialValue) {
      hooks[currentHook] = hooks[currentHook] || initialValue
      const setStateHookIndex = currentHook
      // 這里我們暫且默認(rèn) setState 方式第一個(gè)參數(shù)不傳 函數(shù),直接傳狀態(tài)
      const setState = newState => (hooks[setStateHookIndex] = newState)
      return [hooks[currentHook++], setState]
    }
  }
})()

因此當(dāng)重新渲染 App 的時(shí)候,再次執(zhí)行 useState 的時(shí)候傳入的參數(shù) kevin , 0 也就不會(huì)去使用,而是直接拿之前 hooks 存儲(chǔ)好的值。

hooks 規(guī)則

官網(wǎng) hoos 規(guī)則中明確的提出 hooks 不要再循環(huán),條件或嵌套函數(shù)中使用。

為什么不可以?

我們來看下

下面這樣一段代碼。執(zhí)行 useState 重新渲染,和初始化渲染 順序不一樣就會(huì)出現(xiàn)如下問題

如果了解了上面 useState 模擬寫法的存儲(chǔ)方式,那么這個(gè)問題的原因就迎刃而解了。相關(guān)參考視頻:傳送門

useEffect 解析

useEffect 使用

初始化會(huì) 打印一次 ‘useEffect_execute’, 改變年齡重新render,會(huì)再打印, 改變名字重新 render, 不會(huì)打印。因?yàn)橐蕾嚁?shù)組里面就監(jiān)聽了 age 的值

import React, { useState, useEffect } from 'react';
function App() {
  const [name, setName] = useState('Kevin');
  const [age, setAge] = useState(0);
  const handleName = () => {
    setName('Don');
  };
  const handleAge = () => {
    setAge(age + 1);
  };
  useEffect(()=>{
    console.log('useEffect_execute')
  }, [age])
  return (
    <div>
      <p>姓名: {name}</p>
      <button onClick={handleName}> 改名字 </button>
      <p>年齡: {age}</p>
      <button onClick={handleAge}> 加一歲 </button>
    </div>
  );
}
export default App;

useEffect 的模擬實(shí)現(xiàn)

const MyReact = (function() {
  // 開辟一個(gè)儲(chǔ)存 hooks 的空間
  let hooks = []; 
  // 指針從 0 開始
  let currentHook = 0 ;
  // 定義個(gè)模塊全局的 useEffect 依賴
  let deps;
  return {
    // 偽代碼 解釋重新渲染的時(shí)候 會(huì)初始化 currentHook
    render(Component) {
      const Comp = Component()
      Comp.render()
      currentHook = 0 // 重新渲染時(shí)候改變 hooks 指針
      return Comp
    },      
    useState(initialValue) {
      hooks[currentHook] = hooks[currentHook] || initialValue
      const setStateHookIndex = currentHook
      // 這里我們暫且默認(rèn) setState 方式第一個(gè)參數(shù)不傳 函數(shù),直接傳狀態(tài)
      const setState = newState => (hooks[setStateHookIndex] = newState)
      return [hooks[currentHook++], setState]
    }
    useEffect(callback, depArray) {
      const hasNoDeps = !depArray
      // 如果沒有依賴,說明是第一次渲染,或者是沒有傳入依賴參數(shù),那么就 為 true
      // 有依賴 使用 every 遍歷依賴的狀態(tài)是否變化, 變化就會(huì) true
      const hasChangedDeps = deps ? !depArray.every((el, i) => el === deps[i]) : true
      // 如果沒有依賴, 或者依賴改變
      if (hasNoDeps || hasChangedDeps) {
        // 執(zhí)行 
        callback()
        // 更新依賴
        deps = depArray
      }
    },

  }
})()

useEffect 注意事項(xiàng)

依賴項(xiàng)要真實(shí)

依賴需要想清楚。

剛開始使用 useEffect 的時(shí)候,我只有想重新觸發(fā) useEffect 的時(shí)候才會(huì)去設(shè)置依賴

那么也就會(huì)出現(xiàn)如下的問題。

希望的效果是界面中一秒增加一歲

import React, { useState, useEffect } from 'react';
function App() {
  const [name, setName] = useState('Kevin');
  const [age, setAge] = useState(0);
  const handleName = () => {
    setName('Don');
  };
  const handleAge = () => {
    setAge(age + 1);
  };
  useEffect(() => {
    setInterval(() => {
      setAge(age + 1);
      console.log(age)
    }, 1000);
  }, []);
  return (
    <div>
      <p>姓名: {name}</p>
      <button onClick={handleName}> 改名字 </button>
      <p>年齡: {age}</p>
      <button onClick={handleAge}> 加一歲 </button>
    </div>
  );
}
export default App;

其實(shí)你會(huì)發(fā)現(xiàn) 這里界面就增加了 一次 年齡。究其原因:

**在第一次渲染中,age0。因此,setAge(age+ 1)在第一次渲染中等價(jià)于setAge(0 + 1)。然而我設(shè)置了0依賴為空數(shù)組,那么之后的 useEffect 不會(huì)再重新運(yùn)行,它后面每一秒都會(huì)調(diào)用setAge(0 + 1) **

也就是當(dāng)我們需要 依賴 age 的時(shí)候我們 就必須再 依賴數(shù)組中去記錄他的依賴。這樣useEffect 才會(huì)正常的給我們?nèi)ミ\(yùn)行。

所以我們想要每秒都遞增的話有兩種方法

方法一:

真真切切的把你所依賴的狀態(tài)填寫到 數(shù)組中

  // 通過監(jiān)聽 age 的變化。來重新執(zhí)行 useEffect 內(nèi)的函數(shù)
  // 因此這里也就需要記錄定時(shí)器,當(dāng)卸載的時(shí)候我們?nèi)デ蹇斩〞r(shí)器,防止多個(gè)定時(shí)器重新觸發(fā)
  useEffect(() => {
    const id = setInterval(() => {
      setAge(age + 1);
    }, 1000);
    return () => {
      clearInterval(id)
    };
  }, [age]);

方法二

useState 的參數(shù)傳入 一個(gè)方法。

注:上面我們模擬的 useState 并沒有做這個(gè)處理 后面我會(huì)講解源碼中去解析。

useEffect(() => {
    setInterval(() => {
      setAge(age => age + 1);
    }, 1000);
  }, []);

useEffect 只運(yùn)行了一次,通過 useState 傳入函數(shù)的方式它不再需要知道當(dāng)前的age值。因?yàn)?React render 的時(shí)候它會(huì)幫我們處理

這正是setAge(age => age + 1)做的事情。再重新渲染的時(shí)候他會(huì)幫我們執(zhí)行這個(gè)方法,并且傳入最新的狀態(tài)。

所以我們做到了去時(shí)刻改變狀態(tài),但是依賴中卻不用寫這個(gè)依賴,因?yàn)槲覀儗⒃镜氖褂玫降囊蕾囈瞥恕#ㄟ@句話表達(dá)感覺不到位)

接口無限請(qǐng)求問題

剛開始使用 useEffect 的我,在接口請(qǐng)求的時(shí)候常常會(huì)這樣去寫代碼。

props 里面有 頁碼,通過切換頁碼,希望監(jiān)聽頁碼的變化來重新去請(qǐng)求數(shù)據(jù)

// 以下是偽代碼 
// 這里用 dva 發(fā)送請(qǐng)求來模擬
import React, { useState, useEffect } from 'react';
import { connect } from 'dva';
function App(props) {
  const { goods, dispatch, page } = props;
  useEffect(() => {
    // 頁面完成去發(fā)情請(qǐng)求
   dispatch({
      type: '/goods/list',
      payload: {page, pageSize:10},
    });
    // xxxx 
  }, [props]);
  return (
    <div>
      <p>商品: {goods}</p>
     <button>點(diǎn)擊切下一頁</button>
    </div>
  );
}
export default connect(({ goods }) => ({
  goods,
}))(App);

然后得意洋洋的刷新界面,發(fā)現(xiàn) Network 中瘋狂循環(huán)的請(qǐng)求接口,導(dǎo)致頁面的卡死。

究其原因是因?yàn)樵谝蕾囍?,我們通過接口改變了狀態(tài) props 的更新, 導(dǎo)致重新渲染組件,導(dǎo)致會(huì)重新執(zhí)行 useEffect 里面的方法,方法執(zhí)行完成之后 props 的更新, 導(dǎo)致重新渲染組件,依賴項(xiàng)目是對(duì)象,引用類型發(fā)現(xiàn)不相等,又去執(zhí)行 useEffect 里面的方法,又重新渲染,然后又對(duì)比,又不相等, 又執(zhí)行。因此產(chǎn)生了無限循環(huán)。

Hooks 源碼解析

該源碼位置: react/packages/react-reconciler/src/ReactFiberHooks.js

const Dispatcher={
  useReducer: mountReducer,
  useState: mountState,
  // xxx 省略其他的方法
}

mountState 源碼

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
    /*    mountWorkInProgressHook 方法 返回初始化對(duì)象    {        memoizedState: null,        baseState: null,         queue: null,        baseUpdate: null,        next: null,      }    */
  const hook = mountWorkInProgressHook();
 // 如果傳入的是函數(shù) 直接執(zhí)行,所以第一次這個(gè)參數(shù)是 undefined
  if (typeof initialState === 'function') {
    initialState = initialState();
  }
  hook.memoizedState = hook.baseState = initialState;
  const queue = (hook.queue = {
    last: null,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  });
    /*    定義 dispatch 相當(dāng)于    const dispatch = queue.dispatch =    dispatchAction.bind(null,currentlyRenderingFiber,queue);    */ 
  const dispatch: Dispatch<
    BasicStateAction<S>,
  > = (queue.dispatch = (dispatchAction.bind(
    null,
    // Flow doesn't know this is non-null, but we do.
    ((currentlyRenderingFiber: any): Fiber),
    queue,
  ): any));
 // 可以看到這個(gè)dispatch就是dispatchAction綁定了對(duì)應(yīng)的 currentlyRenderingFiber 和 queue。最后return:
  return [hook.memoizedState, dispatch];
}

dispatchAction 源碼

function dispatchAction<A>(fiber: Fiber, queue: UpdateQueue<A>, action: A) {
  //... 省略驗(yàn)證的代碼
  const alternate = fiber.alternate;
    /*    這其實(shí)就是判斷這個(gè)更新是否是在渲染過程中產(chǎn)生的,currentlyRenderingFiber只有在FunctionalComponent更新的過程中才會(huì)被設(shè)置,在離開更新的時(shí)候設(shè)置為null,所以只要存在并更產(chǎn)生更新的Fiber相等,說明這個(gè)更新是在當(dāng)前渲染中產(chǎn)生的,則這是一次reRender。所有更新過程中產(chǎn)生的更新記錄在renderPhaseUpdates這個(gè)Map上,以每個(gè)Hook的queue為key。對(duì)于不是更新過程中產(chǎn)生的更新,則直接在queue上執(zhí)行操作就行了,注意在最后會(huì)發(fā)起一次scheduleWork的調(diào)度。    */
  if (
    fiber === currentlyRenderingFiber ||
    (alternate !== null && alternate === currentlyRenderingFiber)
  ) {
    didScheduleRenderPhaseUpdate = true;
    const update: Update<A> = {
      expirationTime: renderExpirationTime,
      action,
      next: null,
    };
    if (renderPhaseUpdates === null) {
      renderPhaseUpdates = new Map();
    }
    const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
    if (firstRenderPhaseUpdate === undefined) {
      renderPhaseUpdates.set(queue, update);
    } else {
      // Append the update to the end of the list.
      let lastRenderPhaseUpdate = firstRenderPhaseUpdate;
      while (lastRenderPhaseUpdate.next !== null) {
        lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
      }
      lastRenderPhaseUpdate.next = update;
    }
  } else {
    const currentTime = requestCurrentTime();
    const expirationTime = computeExpirationForFiber(currentTime, fiber);
    const update: Update<A> = {
      expirationTime,
      action,
      next: null,
    };
    flushPassiveEffects();
    // Append the update to the end of the list.
    const last = queue.last;
    if (last === null) {
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      const first = last.next;
      if (first !== null) {
        // Still circular.
        update.next = first;
      }
      last.next = update;
    }
    queue.last = update;
    scheduleWork(fiber, expirationTime);
  }
}

mountReducer 源碼

多勒第三個(gè)參數(shù),是函數(shù)執(zhí)行,默認(rèn)初始狀態(tài) undefined

其他的和 上面的 mountState 大同小異

function mountReducer<S, I, A>(
  reducer: (S, A) => S,
  initialArg: I,
  init?: I => S,
): [S, Dispatch<A>] {
  const hook = mountWorkInProgressHook();
  let initialState;
  if (init !== undefined) {
    initialState = init(initialArg);
  } else {
    initialState = ((initialArg: any): S);
  }
    // 其他和 useState 一樣
  hook.memoizedState = hook.baseState = initialState;
  const queue = (hook.queue = {
    last: null,
    dispatch: null,
    lastRenderedReducer: reducer,
    lastRenderedState: (initialState: any),
  });
  const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
    null,
    // Flow doesn't know this is non-null, but we do.
    ((currentlyRenderingFiber: any): Fiber),
    queue,
  ): any));
  return [hook.memoizedState, dispatch];
}

通過 react 源碼中,可以看出 useState 是特殊的 useReducer

  • 可見useState不過就是個(gè)語法糖,本質(zhì)其實(shí)就是useReducer
  • updateState 復(fù)用了 updateReducer(區(qū)別只是 updateState 將 reducer 設(shè)置為 updateReducer)
  • mountState 雖沒直接調(diào)用 mountReducer,但是幾乎大同小異(區(qū)別只是 mountState 將 reducer 設(shè)置為basicStateReducer)

注:這里僅是 react 源碼,至于重新渲染這塊 react-dom 還沒有去深入了解。

更新:

分兩種情況,是否是 reRender,所謂reRender就是說在當(dāng)前更新周期中又產(chǎn)生了新的更新,就繼續(xù)執(zhí)行這些更新知道當(dāng)前渲染周期中沒有更新為止

他們基本的操作是一致的,就是根據(jù) reducerupdate.action 來創(chuàng)建新的 state,并賦值給Hook.memoizedState 以及 Hook.baseState

注意這里,對(duì)于非reRender得情況,我們會(huì)對(duì)每個(gè)更新判斷其優(yōu)先級(jí),如果不是當(dāng)前整體更新優(yōu)先級(jí)內(nèi)得更新會(huì)跳過,第一個(gè)跳過得Update會(huì)變成新的baseUpdate,他記錄了在之后所有得Update,即便是優(yōu)先級(jí)比他高得,因?yàn)樵谒粓?zhí)行得時(shí)候,需要保證后續(xù)的更新要在他更新之后的基礎(chǔ)上再次執(zhí)行,因?yàn)榻Y(jié)果可能會(huì)不一樣。

來源

preact 中的 hooks

Preact 最優(yōu)質(zhì)的開源 React 替代品?。ㄝp量級(jí) 3kb)

注意:這里的替代是指如果不用 react 的話,可以使用這個(gè)。而不是取代。

useState 源碼解析

調(diào)用了 useReducer 源碼

export function useState(initialState) {
    return useReducer(invokeOrReturn, initialState);
}

useReducer 源碼解析

// 模塊全局定義
/** @type {number} */
let currentIndex; // 狀態(tài)的索引,也就是前面模擬實(shí)現(xiàn) useState 時(shí)候所說的指針
let currentComponent; // 當(dāng)前的組件
export function useReducer(reducer, initialState, init) {
    /** @type {import('./internal').ReducerHookState} */
    // 通過 getHookState 方法來獲取 hooks 
    const hookState = getHookState(currentIndex++);
    // 如果沒有組件 也就是初始渲染
    if (!hookState._component) {
        hookState._component = currentComponent;
        hookState._value = [
            // 沒有 init 執(zhí)行 invokeOrReturn
                // invokeOrReturn 方法判斷 initialState 是否是函數(shù)
                // 是函數(shù) initialState(null) 因?yàn)槌跏蓟瘺]有值默認(rèn)為null
                // 不是函數(shù) 直接返回 initialState
            !init ? invokeOrReturn(null, initialState) : init(initialState),
            action => {
                // reducer == invokeOrReturn
                const nextValue = reducer(hookState._value[0], action);
                // 如果當(dāng)前的值,不等于 下一個(gè)值
                // 也就是更新的狀態(tài)的值,不等于之前的狀態(tài)的值
                if (hookState._value[0]!==nextValue) {
                    // 儲(chǔ)存最新的狀態(tài)
                    hookState._value[0] = nextValue;
                    // 渲染組件
                    hookState._component.setState({});
                }
            }
        ];
    }
    // hookState._value 數(shù)據(jù)格式也就是 [satea:any, action:Function] 的數(shù)據(jù)格式拉
    return hookState._value;
}

getHookState 方法

function getHookState(index) {
    if (options._hook) options._hook(currentComponent);
    const hooks = currentComponent.__hooks || (currentComponent.__hooks = { _list: [], _pendingEffects: [], _pendingLayoutEffects: [] });

    if (index >= hooks._list.length) {
        hooks._list.push({});
    }
    return hooks._list[index];
}

invokeOrReturn 方法

function invokeOrReturn(arg, f) {
    return typeof f === 'function' ? f(arg) : f;
}

總結(jié)

使用 hooks 幾個(gè)月了。基本上所有類組件我都使用函數(shù)式組件來寫?,F(xiàn)在 react 社區(qū)的很多組件,都也開始支持hooks。大概了解了點(diǎn)重要的源碼,做到知其然也知其所以然,那么在實(shí)際工作中使用他可以減少不必要的 bug,提高效率。

到此這篇關(guān)于React深入淺出分析Hooks源碼的文章就介紹到這了,更多相關(guān)React Hooks內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React18新增特性介紹

    React18新增特性介紹

    react歷次版本迭代主要想解決的是兩類導(dǎo)致網(wǎng)頁卡頓的問題,分別是cpu密集型任務(wù)和io密集型任務(wù)導(dǎo)致的卡頓問題,react18新增特性就是為了解決上述問題
    2022-09-09
  • React實(shí)現(xiàn)pc端的彈出框效果

    React實(shí)現(xiàn)pc端的彈出框效果

    這篇文章主要為大家詳細(xì)介紹了React實(shí)現(xiàn)pc端的彈出框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • React應(yīng)用中避免白屏現(xiàn)象的方法小結(jié)

    React應(yīng)用中避免白屏現(xiàn)象的方法小結(jié)

    在開發(fā)React應(yīng)用程序時(shí),我們都曾遇到過這樣的場(chǎng)景:一個(gè)未被捕獲的異常突然中斷了組件的渲染流程,導(dǎo)致用戶界面呈現(xiàn)出一片空白,也就是俗稱的“白屏”現(xiàn)象,本文將探討如何在React應(yīng)用中有效捕獲并處理這些錯(cuò)誤,避免白屏現(xiàn)象的發(fā)生,需要的朋友可以參考下
    2024-06-06
  • ReactJS中使用TypeScript的方法

    ReactJS中使用TypeScript的方法

    TypeScript 實(shí)際上就是具有強(qiáng)類型的 JavaScript,可以對(duì)類型進(jìn)行強(qiáng)校驗(yàn),好處是代碼閱讀起來比較清晰,代碼類型出現(xiàn)問題時(shí),在編譯時(shí)就可以發(fā)現(xiàn),而不會(huì)在運(yùn)行時(shí)由于類型的錯(cuò)誤而導(dǎo)致報(bào)錯(cuò),這篇文章主要介紹了ReactJS中使用TypeScript的方法,需要的朋友可以參考下
    2024-04-04
  • 淺談在react中如何實(shí)現(xiàn)掃碼槍輸入

    淺談在react中如何實(shí)現(xiàn)掃碼槍輸入

    這篇文章主要介紹了淺談在react中如何實(shí)現(xiàn)掃碼槍輸入,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • react native與webview通信的示例代碼

    react native與webview通信的示例代碼

    本篇文章主要介紹了react native與webview通信的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • React經(jīng)典面試題之倒計(jì)時(shí)組件詳解

    React經(jīng)典面試題之倒計(jì)時(shí)組件詳解

    這些天也都在面試,面試的內(nèi)容也大多千篇一律,無外乎vue、react這些框架的一些原理,和使用方法,但是也遇到些有趣的題目,這篇文章主要給大家介紹了關(guān)于React經(jīng)典面試題之倒計(jì)時(shí)組件的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • React 服務(wù)器組件的使用方法詳解

    React 服務(wù)器組件的使用方法詳解

    最近,React 服務(wù)器組件受到了廣泛的關(guān)注和熱捧,這是因?yàn)?nbsp;React 服務(wù)器組件允許開發(fā)人員將與組件相關(guān)的任務(wù)外包給服務(wù)器,在本文中,我們將討論什么是 React 服務(wù)器組件,以及如何將它們集成到構(gòu)建應(yīng)用程序中,需要的朋友可以參考下
    2023-10-10
  • D3.js(v3)+react 實(shí)現(xiàn)帶坐標(biāo)與比例尺的柱形圖 (V3版本)

    D3.js(v3)+react 實(shí)現(xiàn)帶坐標(biāo)與比例尺的柱形圖 (V3版本)

    這篇文章主要介紹了D3.js(v3)+react 制作 一個(gè)帶坐標(biāo)與比例尺的柱形圖 (V3版本) ,本文通過實(shí)例代碼文字相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2019-05-05
  • Taro?React自定義TabBar使用useContext解決底部選中異常

    Taro?React自定義TabBar使用useContext解決底部選中異常

    這篇文章主要為大家介紹了Taro?React底部自定義TabBar使用React?useContext解決底部選中異常(需要點(diǎn)兩次才能選中的問題)示例詳解,有需要的朋友可以借鑒參考下
    2023-08-08

最新評(píng)論