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

深入研究React中setState源碼

 更新時間:2017年11月17日 16:27:42   作者:jimwmg  
這篇文章主要介紹了深入研究React中setState源碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

React作為一門前端框架,雖然只是focus在MVVM中的View部分,但還是實現(xiàn)了View和model的綁定。修改數(shù)據(jù)的同時,可以實現(xiàn)View的刷新。這大大簡化了我們的邏輯,只用關(guān)心數(shù)據(jù)流的變化,同時減少了代碼量,使得后期維護(hù)也更加方便。這個特性則要歸功于setState()方法。React中利用隊列機(jī)制來管理state,避免了很多重復(fù)的View刷新。下面我們來從源碼角度探尋下setState機(jī)制。

1 還是先聲明一個組件,從最開始一步步來尋源;

class App extends Component {
  //只在組件重新加載的時候執(zhí)行一次
  constructor(props) {
    super(props);
   //..
  }
   //other methods
}
//ReactBaseClasses.js中如下:這里就是setState函數(shù)的來源;
//super其實就是下面這個函數(shù)
function ReactComponent(props, context, updater) {
 this.props = props;
 this.context = context;
 this.refs = emptyObject;
 // We initialize the default updater but the real one gets injected by the
 // renderer.
 this.updater = updater || ReactNoopUpdateQueue;
}
ReactComponent.prototype.setState = function (partialState, callback) {
 this.updater.enqueueSetState(this, partialState);
 if (callback) {
  this.updater.enqueueCallback(this, callback, 'setState');
 }
};

所以主要來看是否傳入了updater參數(shù),也就是說何時進(jìn)行 new 組件;具體的updater參數(shù)是怎么傳遞進(jìn)來的,以及是那個對象,參見

react源碼分析系列文章下面的react中context updater到底是如何傳遞的

這里直接說結(jié)果,updater對象其實就是ReactUpdateQueue.js 中暴漏出的ReactUpdateQueue對象;

2 既然找到了setState之后執(zhí)行的動作,我們在一步步深入進(jìn)去

class Root extends React.Component {
 constructor(props) {
  super(props);
  this.state = {
   count: 0
  };
 }
 componentDidMount() {
  let me = this;
  me.setState({
   count: me.state.count + 1
  });
  console.log(me.state.count);  // 打印出0
  me.setState({
   count: me.state.count + 1
  });
  console.log(me.state.count);  // 打印出0
  setTimeout(function(){
   me.setState({
    count: me.state.count + 1
   });
   console.log(me.state.count);  // 打印出2
  }, 0);
  setTimeout(function(){
   me.setState({
    count: me.state.count + 1
   });
   console.log(me.state.count);  // 打印出3
  }, 0);
 }
 render() {
  return (
   <h1>{this.state.count}</h1>
  )
 }
}

ReactComponent.prototype.setState = function (partialState, callback) {
 this.updater.enqueueSetState(this, partialState);
 if (callback) {
  this.updater.enqueueCallback(this, callback, 'setState');
 }
};

ReactUpdateQueue.js

var ReactUpdates = require('./ReactUpdates');

function enqueueUpdate(internalInstance) {
 ReactUpdates.enqueueUpdate(internalInstance);
};
function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
 //在ReactCompositeComponent.js中有這樣一行代碼,這就是其來源;
 // Store a reference from the instance back to the internal representation
  //ReactInstanceMap.set(inst, this);
 var internalInstance = ReactInstanceMap.get(publicInstance);
 //返回的是在ReactCompositeComponent.js中construct函數(shù)返回的對象;ReactInstance實例對象并不是簡單的new 我們寫的組件的實例對象,而是經(jīng)過instantiateReactComponent.js中ReactCompositeComponentWrapper函數(shù)包裝的對象;詳見 創(chuàng)建React組件方式以及源碼分析.md
 return internalInstance;
};
var ReactUpdateQueue = {
//。。。。省略其他代碼
 enqueueCallback: function (publicInstance, callback, callerName) {
  ReactUpdateQueue.validateCallback(callback, callerName);
  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance);
  if (!internalInstance) {
   return null;
  }
//這里將callback放入組件實例的_pendingCallbacks數(shù)組中;
  if (internalInstance._pendingCallbacks) {
   internalInstance._pendingCallbacks.push(callback);
  } else {
   internalInstance._pendingCallbacks = [callback];
  }
  // TODO: The callback here is ignored when setState is called from
  // componentWillMount. Either fix it or disallow doing so completely in
  // favor of getInitialState. Alternatively, we can disallow
  // componentWillMount during server-side rendering.
  enqueueUpdate(internalInstance);
 },

 enqueueSetState: function (publicInstance, partialState) {
  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
  if (!internalInstance) {
   return;
  }
  //這里,初始化queue變量,同時初始化 internalInstance._pendingStateQueue = [ ] ;
  //對于 || 的短路運(yùn)算還是要多梳理下
  //queue數(shù)組(模擬隊列)中存放著setState放進(jìn)來的對象;
  var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
  //這里將partialState放入queue數(shù)組中,也就是internalInstance._pendingStateQueue 數(shù)組中,此時,每次setState的partialState,都放進(jìn)了React組件實例對象上的_pendingStateQueue屬性中,成為一個數(shù)組;
  queue.push(partialState);

  enqueueUpdate(internalInstance);
 },
};

module.exports = ReactUpdateQueue;

可以看到enqueueSetState enqueueCallback 最后都會執(zhí)行enqueueUpdate;

function enqueueUpdate(internalInstance) {
 ReactUpdates.enqueueUpdate(internalInstance);
}

ReactUpdates.js

var dirtyComponents = [];
var updateBatchNumber = 0;
var asapCallbackQueue = CallbackQueue.getPooled();
var asapEnqueued = false;
//這里聲明batchingStrategy為null,后期通過注冊給其賦值;
var batchingStrategy = null;
//這里的component參數(shù)是js中ReactCompositeComponentWrapper函數(shù)包裝的后的React組件實例對象;
function enqueueUpdate(component) {
 ensureInjected();
//第一次執(zhí)行setState的時候,可以進(jìn)入if語句,遇到里面的return語句,終止執(zhí)行
 //如果不是正處于創(chuàng)建或更新組件階段,則處理update事務(wù)

 if (!batchingStrategy.isBatchingUpdates) {
  //batchedUpdates就是ReactDefaultBatchingStrategy.js中聲明的
  batchingStrategy.batchedUpdates(enqueueUpdate, component);
  return;
 }
//第二次執(zhí)行setState的時候,進(jìn)入不了if語句,將組件放入dirtyComponents
 //如果正在創(chuàng)建或更新組件,則暫且先不處理update,只是將組件放在dirtyComponents數(shù)組中

 dirtyComponents.push(component);
 if (component._updateBatchNumber == null) {
  component._updateBatchNumber = updateBatchNumber + 1;
 }
};
//enqueueUpdate包含了React避免重復(fù)render的邏輯。mountComponent和updateComponent方法在執(zhí)行的最開始,會調(diào)用到batchedUpdates進(jìn)行批處理更新,此時會將isBatchingUpdates設(shè)置為true,也就是將狀態(tài)標(biāo)記為現(xiàn)在正處于更新階段了。之后React以事務(wù)的方式處理組件update,事務(wù)處理完后會調(diào)用wrapper.close(), 而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES這個wrapper,故最終會調(diào)用RESET_BATCHED_UPDATES.close(), 它最終會將isBatchingUpdates設(shè)置為false。

ReactDefaultBatchingStrategy.js

//RESET_BATCHED_UPDATES用來管理isBatchingUpdates的狀態(tài)
var RESET_BATCHED_UPDATES = {
 initialize: emptyFunction,
 close: function () {
  // 事務(wù)批更新處理結(jié)束時,將isBatchingUpdates設(shè)為了false
  ReactDefaultBatchingStrategy.isBatchingUpdates = false;
 }
};
//FLUSH_BATCHED_UPDATES會在一個transaction的close階段運(yùn)行runBatchedUpdates,從而執(zhí)行update。
//因為close的執(zhí)行順序是FLUSH_BATCHED_UPDATES.close ==> 然后RESET_BATCHED_UPDATES.close
var FLUSH_BATCHED_UPDATES = {
 initialize: emptyFunction,
 close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

function ReactDefaultBatchingStrategyTransaction() {
 this.reinitializeTransaction();
}

_assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, {
 getTransactionWrappers: function () {
  return TRANSACTION_WRAPPERS;
 }
});
//這個transition就是下面ReactDefaultBatchingStrategy對象中使用的transaction變量
var transaction = new ReactDefaultBatchingStrategyTransaction();
var ReactDefaultBatchingStrategy = {
 isBatchingUpdates: false,

 /**
  * Call the provided function in a context within which calls to `setState`
  * and friends are batched such that components aren't updated unnecessarily.
  */
 batchedUpdates: function (callback, a, b, c, d, e) {
  var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
// 批處理最開始時,將isBatchingUpdates設(shè)為true,表明正在更新
  ReactDefaultBatchingStrategy.isBatchingUpdates = true;

  // The code is written this way to avoid extra allocations
  if (alreadyBatchingUpdates) {
   return callback(a, b, c, d, e);
  } else {
   //transition在上面已經(jīng)聲明; // 以事務(wù)的方式處理updates,后面詳細(xì)分析transaction
   return transaction.perform(callback, null, a, b, c, d, e);
  }
 }
};

module.exports = ReactDefaultBatchingStrategy;

接下來我們看下React中的事物處理機(jī)制到底是如何運(yùn)行的;

Transaction.js

var _prodInvariant = require('./reactProdInvariant');
var invariant = require('fbjs/lib/invariant');
var OBSERVED_ERROR = {};
var TransactionImpl = {
 reinitializeTransaction: function () {
  //getTransactionWrappers這個函數(shù)ReactDefaultBatchingStrategy.js中聲明的,上面有;返回一個數(shù)組;
  this.transactionWrappers = this.getTransactionWrappers();
  if (this.wrapperInitData) {
   this.wrapperInitData.length = 0;
  } else {
   this.wrapperInitData = [];
  }
  this._isInTransaction = false;
 },

 _isInTransaction: false,
 getTransactionWrappers: null,
 isInTransaction: function () {
  return !!this._isInTransaction;
 },
 perform: function (method, scope, a, b, c, d, e, f) {
  var errorThrown;
  var ret;
  try {
   this._isInTransaction = true;
   errorThrown = true;
   //var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
   //1 這里會先執(zhí)行所有的TRANSACTION_WRAPPERS中成員的initialize方法,上面聲明的其都是emptyFunction
   this.initializeAll(0);
   //2 這里其實還是執(zhí)行的 enqueueUpdate 函數(shù)
   ret = method.call(scope, a, b, c, d, e, f);
   errorThrown = false;
  } finally {
   try {
    if (errorThrown) {
     // If `method` throws, prefer to show that stack trace over any thrown
     // by invoking `closeAll`.
     try {
      this.closeAll(0);
     } catch (err) {}
    } else {
     // Since `method` didn't throw, we don't want to silence the exception
     // here.
     //3 執(zhí)行TRANSACTION_WRAPPERS對象中成員的所有close方法;
     this.closeAll(0);
    }
   } finally {
    this._isInTransaction = false;
   }
  }
  return ret;
 },

 initializeAll: function (startIndex) {
  var transactionWrappers = this.transactionWrappers;
  for (var i = startIndex; i < transactionWrappers.length; i++) {
   var wrapper = transactionWrappers[i];
   try {
    
    this.wrapperInitData[i] = OBSERVED_ERROR;
    this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
   } finally {
    if (this.wrapperInitData[i] === OBSERVED_ERROR) {
     
     try {
      this.initializeAll(i + 1);
     } catch (err) {}
    }
   }
  }
 },
 closeAll: function (startIndex) {
  var transactionWrappers = this.transactionWrappers;
  for (var i = startIndex; i < transactionWrappers.length; i++) {
   var wrapper = transactionWrappers[i];
   var initData = this.wrapperInitData[i];
   var errorThrown;
   try {
  
    errorThrown = true;
    if (initData !== OBSERVED_ERROR && wrapper.close) {
     wrapper.close.call(this, initData);
    }
    errorThrown = false;
   } finally {
    if (errorThrown) {
    
     try {
      this.closeAll(i + 1);
     } catch (e) {}
    }
   }
  }
  this.wrapperInitData.length = 0;
 }
};

module.exports = TransactionImpl

//3 執(zhí)行TRANSACTION_WRAPPERS對象中成員的所有close方法;
var FLUSH_BATCHED_UPDATES = {
 initialize: emptyFunction,
 close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

接著會執(zhí)行ReactUpdates.js中的flushBatchedUpdates方法

ReactUpdates.js中

var flushBatchedUpdates = function () {
 
 while (dirtyComponents.length || asapEnqueued) {
  if (dirtyComponents.length) {
   var transaction = ReactUpdatesFlushTransaction.getPooled();
   //這里執(zhí)行runBatchedUpdates函數(shù);
   transaction.perform(runBatchedUpdates, null, transaction);
   ReactUpdatesFlushTransaction.release(transaction);
  }

  if (asapEnqueued) {
   asapEnqueued = false;
   var queue = asapCallbackQueue;
   asapCallbackQueue = CallbackQueue.getPooled();
   queue.notifyAll();
   CallbackQueue.release(queue);
  }
 }
};
function runBatchedUpdates(transaction) {
 var len = transaction.dirtyComponentsLength;
 
 dirtyComponents.sort(mountOrderComparator);

 updateBatchNumber++;

 for (var i = 0; i < len; i++) {
 
  var component = dirtyComponents[i];
  var callbacks = component._pendingCallbacks;
  component._pendingCallbacks = null;

  var markerName;
  if (ReactFeatureFlags.logTopLevelRenders) {
   var namedComponent = component;
   // Duck type TopLevelWrapper. This is probably always true.
   if (component._currentElement.type.isReactTopLevelWrapper) {
    namedComponent = component._renderedComponent;
   }
   markerName = 'React update: ' + namedComponent.getName();
   console.time(markerName);
  }
//這里才是真正的開始更新組件
  ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction, updateBatchNumber);

  if (markerName) {
   console.timeEnd(markerName);
  }

  if (callbacks) {
   for (var j = 0; j < callbacks.length; j++) {
    transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
   }
  }
 }
}

ReactReconciler.js中

performUpdateIfNecessary: function (internalInstance, transaction, updateBatchNumber) {
  if (internalInstance._updateBatchNumber !== updateBatchNumber) {
   // The component's enqueued batch number should always be the current
   // batch or the following one.
   return;
  }
 //這里執(zhí)行React組件實例對象的更新;internalInstance上的performUpdateIfNecessary在ReactCompositeComponent.js中的;
  internalInstance.performUpdateIfNecessary(transaction);
  if (process.env.NODE_ENV !== 'production') {
   if (internalInstance._debugID !== 0) {
    ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
   }
  }
 }

ReactCompositeComponent.js

performUpdateIfNecessary: function (transaction) {
 if (this._pendingElement != null) {
  // receiveComponent會最終調(diào)用到updateComponent,從而刷新View
  ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
 } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
  // 執(zhí)行updateComponent,從而刷新View。
  this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
 } else {
  this._updateBatchNumber = null;
 }
},

 //執(zhí)行更新React組件的props. state。context函數(shù)

 updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
  var inst = this._instance;
  var willReceive = false;
  var nextContext;
  // Determine if the context has changed or not
  if (this._context === nextUnmaskedContext) {
   nextContext = inst.context;
  } else {
   nextContext = this._processContext(nextUnmaskedContext);
   willReceive = true;
  }

  var prevProps = prevParentElement.props;
  var nextProps = nextParentElement.props;

  // Not a simple state update but a props update
  if (prevParentElement !== nextParentElement) {
   willReceive = true;
  }

  // An update here will schedule an update but immediately set
  // _pendingStateQueue which will ensure that any state updates gets
  // immediately reconciled instead of waiting for the next batch.
  if (willReceive && inst.componentWillReceiveProps) {
   if (process.env.NODE_ENV !== 'production') {
    measureLifeCyclePerf(function () {
     return inst.componentWillReceiveProps(nextProps, nextContext);
    }, this._debugID, 'componentWillReceiveProps');
   } else {
    inst.componentWillReceiveProps(nextProps, nextContext);
   }
  }
//這里可以知道為什么setState可以接受函數(shù),主要就是_processPendingState函數(shù);
  //這里僅僅是將每次setState放入到_pendingStateQueue隊列中的值,合并到nextState,并沒有真正的更新state的值;真正更新組件的state的值是在下面;
  var nextState = this._processPendingState(nextProps, nextContext);
  var shouldUpdate = true;

  if (!this._pendingForceUpdate) {
   if (inst.shouldComponentUpdate) {
    if (process.env.NODE_ENV !== 'production') {
     shouldUpdate = measureLifeCyclePerf(function () {
      return inst.shouldComponentUpdate(nextProps, nextState, nextContext);
     }, this._debugID, 'shouldComponentUpdate');
    } else {
     shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
    }
   } else {
    if (this._compositeType === CompositeTypes.PureClass) {
     shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
    }
   }
  }

  this._updateBatchNumber = null;
  if (shouldUpdate) {
   this._pendingForceUpdate = false;
   // Will set `this.props`, `this.state` and `this.context`.
   this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
  } else {
   // If it's determined that a component should not update, we still want
   // to set props and state but we shortcut the rest of the update.
   //諾:在這里更新組件的state. props 等值;
   this._currentElement = nextParentElement;
   this._context = nextUnmaskedContext;
   inst.props = nextProps;
   inst.state = nextState;
   inst.context = nextContext;
  }
 },


_processPendingState: function (props, context) {
 var inst = this._instance;
 var queue = this._pendingStateQueue;
 var replace = this._pendingReplaceState;
 this._pendingReplaceState = false;
 this._pendingStateQueue = null;

 if (!queue) {
  return inst.state;
 }

 if (replace && queue.length === 1) {
  return queue[0];
 }

 var nextState = _assign({}, replace ? queue[0] : inst.state);
 for (var i = replace ? 1 : 0; i < queue.length; i++) {
  var partial = queue[i];
  //如果是setState的參數(shù)是一個函數(shù),那么該函數(shù)接受三個參數(shù),分別是state props context
  _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
 }

 return nextState;
},

this.state的更新會在_processPendingState執(zhí)行完執(zhí)行。所以兩次setState取到的都是this.state.count最初的值0,這就解釋了之前的現(xiàn)象。其實,這也是React為了解決這種前后state依賴但是state又沒及時更新的一種方案,因此在使用時大家要根據(jù)實際情況來判斷該用哪種方式傳參。來看個小例子直觀感受下

handleClickOnLikeButton () {
  this.setState({ count: 0 }) // => this.state.count 還是 undefined
  this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN
  this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN
 }
//....VS ....
handleClickOnLikeButton () {
  this.setState((prevState) => {
   return { count: 0 }
  })
  this.setState((prevState) => {
   return { count: prevState.count + 1 } // 上一個 setState 的返回是 count 為 0,當(dāng)前返回 1
  })
  this.setState((prevState) => {
   return { count: prevState.count + 2 } // 上一個 setState 的返回是 count 為 1,當(dāng)前返回 3
  })
  // 最后的結(jié)果是 this.state.count 為 3
 }
...

setState流程還是很復(fù)雜的,設(shè)計也很精巧,避免了重復(fù)無謂的刷新組件。它的主要流程如下

  1. enqueueSetState將state放入隊列中,并調(diào)用enqueueUpdate處理要更新的Component
  2. 如果組件當(dāng)前正處于update事務(wù)中,則先將Component存入dirtyComponent中。否則調(diào)用batchedUpdates處理。
  3. batchedUpdates發(fā)起一次transaction.perform()事務(wù)
  4. 開始執(zhí)行事務(wù)初始化,運(yùn)行,結(jié)束三個階段
    1. 初始化:事務(wù)初始化階段沒有注冊方法,故無方法要執(zhí)行
    2. 運(yùn)行:執(zhí)行setSate時傳入的callback方法,一般不會傳callback參數(shù)
    3. 結(jié)束:更新isBatchingUpdates為false,并執(zhí)行FLUSH_BATCHED_UPDATES這個wrapper中的close方法
  5. FLUSH_BATCHED_UPDATES在close階段,會循環(huán)遍歷所有的dirtyComponents,調(diào)用updateComponent刷新組件,并執(zhí)行它的pendingCallbacks, 也就是setState中設(shè)置的callback。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • React?Flux與Redux設(shè)計及使用原理

    React?Flux與Redux設(shè)計及使用原理

    這篇文章主要介紹了React?Flux與Redux設(shè)計及使用,Redux最主要是用作應(yīng)用狀態(tài)的管理。簡言之,Redux用一個單獨的常量狀態(tài)樹(state對象)保存這一整個應(yīng)用的狀態(tài),這個對象不能直接被改變
    2023-03-03
  • react-dnd?API拖拽工具詳細(xì)用法示例

    react-dnd?API拖拽工具詳細(xì)用法示例

    這篇文章主要為大家介紹了react-dnd?API拖拽工具的詳細(xì)用法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 基于React.js實現(xiàn)簡單的文字跑馬燈效果

    基于React.js實現(xiàn)簡單的文字跑馬燈效果

    剛好手上有一個要實現(xiàn)文字跑馬燈的react項目,然后ant-design上面沒有這個組件,于是只能自己手?jǐn)]一個,文中的實現(xiàn)方法講解詳細(xì),希望對大家有所幫助
    2023-01-01
  • React.js?Gird?布局編寫鍵盤組件

    React.js?Gird?布局編寫鍵盤組件

    這篇文章主要介紹了React.js?Gird?布局編寫鍵盤組件,Grid?布局則是將容器劃分成"行"和"列",產(chǎn)生單元格,然后指定"項目所在"的單元格,可以看作是二維布局
    2022-09-09
  • React路由封裝的實現(xiàn)淺析

    React路由封裝的實現(xiàn)淺析

    路由是React項目中相當(dāng)重要的概念,對于功能較為復(fù)雜的網(wǎng)頁來說,必然會涉及到不同功能間的頁面跳轉(zhuǎn),本篇文章將對React官方維護(hù)的路由庫React-Router-Dom的使用和常用組件進(jìn)行講解,同時對路由組件傳遞param參數(shù)的方式進(jìn)行講解,希望對各位讀者有所參考
    2022-08-08
  • React useCallback詳細(xì)使用教程

    React useCallback詳細(xì)使用教程

    useCallback是react中比較重要的一個hook,useCallback 用來返回一個函數(shù),在父子組件傳參或者通用函數(shù)封裝中,起到舉足輕重的作用
    2022-11-11
  • react項目中使用react-dnd實現(xiàn)列表的拖拽排序功能

    react項目中使用react-dnd實現(xiàn)列表的拖拽排序功能

    這篇文章主要介紹了react項目中使用react-dnd實現(xiàn)列表的拖拽排序,本文結(jié)合實例代碼講解react-dnd是如何實現(xiàn),代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • react中使用ant組件庫的modal彈窗報錯問題及解決

    react中使用ant組件庫的modal彈窗報錯問題及解決

    這篇文章主要介紹了react中使用ant組件庫的modal彈窗報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • React+CSS?實現(xiàn)繪制豎狀柱狀圖

    React+CSS?實現(xiàn)繪制豎狀柱狀圖

    這篇文章主要介紹了React+CSS?實現(xiàn)繪制豎狀柱狀圖,文章圍繞主題展開詳細(xì)的內(nèi)容介紹。具有一定的參考價值,需要的朋友可以參考一下
    2022-09-09
  • 解決React報錯Style prop value must be an object

    解決React報錯Style prop value must be a

    這篇文章主要為大家介紹了React報錯Style prop value must be an object解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評論