react usecontext_Vue3原理实战运用,我用40行代码把他装进了React做状态管理
前言
vue-next是Vue3的源碼倉庫,Vue3采用lerna做package的劃分,而響應(yīng)式能力@vue/reactivity被劃分到了單獨的一個package中。
如果我們想把它集成到React中,可行嗎?來試一試吧。
使用示例
話不多說,先看看怎么用的解解饞吧。
//?store.tsimport?{?reactive,?computed,?effect?}?from?'@vue/reactivity';
export?const?state?=?reactive({
??count:?0,
});
const?plusOne?=?computed(()?=>?state.count?+?1);
effect(()?=>?{
??console.log('plusOne?changed:?',?plusOne);
});
const?add?=?()?=>?(state.count?+=?1);
export?const?mutations?=?{
??//?mutation
??add,
};
export?const?store?=?{
??state,
??computed:?{
????plusOne,
??},
};
export?type?Store?=?typeof?store;
//?Index.tsx
import?{?Provider,?useStore?}?from?'rxv'
import?{?mutations,?store,?Store?}?from?'./store.ts'
function?Count()?{
??const?countState?=?useStore((store:?Store)?=>?{
????const?{?state,?computed?}?=?store;
????const?{?count?}?=?state;
????const?{?plusOne?}?=?computed;
????return?{
??????count,
??????plusOne,
????};
??});
??return?(
????<Card?hoverable?style={{?marginBottom:?24?}}><h1>計數(shù)器h1><div?className="chunk"><div?className="chunk">store中的count現(xiàn)在是?{countState.count}div><div?className="chunk">computed值中的plusOne現(xiàn)在是?{countState.plusOne.value}div><Button?onClick={mutations.add}>addButton>div>Card>
??);
}
export?default?()?=>?{
??return?(
????<Provider?value={store}><Count?/>Provider>
??);
};
可以看出,store的定義只用到了@vue/reactivity,而rxv只是在組件中做了一層橋接,連通了Vue3和React,然后我們就可以盡情的使用Vue3的響應(yīng)式能力啦。
預(yù)覽
gif可以看到,完美的利用了reactive、computed的強大能力。
分析
從這個包提供的幾個核心api來分析:
effect(重點)
effect其實是響應(yīng)式庫中一個通用的概念:觀察函數(shù),就像Vue2中的Watcher,mobx中的autorun,observer一樣,它的作用是收集依賴。
它接受的是一個函數(shù),它會幫你執(zhí)行這個函數(shù),并且開啟依賴收集,
這個函數(shù)內(nèi)部對于響應(yīng)式數(shù)據(jù)的訪問都可以收集依賴,那么在響應(yīng)式數(shù)據(jù)被修改后,就會觸發(fā)更新。
最簡單的用法
const?data?=?reactive({?count:?0?})effect(()?=>?{
????//?就是這句話?訪問了data.count
????//?從而收集到了依賴
????console.log(data.count)
})
data.count?=?1
//?控制臺打印出1
那么如果把這個簡單例子中的
()?=>?{????//?就是這句話?訪問了data.count
????//?從而收集到了依賴
????console.log(data.count)
}
這個函數(shù),替換成React的組件渲染,是不是就能達成響應(yīng)式更新組件的目的了?
reactive(重點)
響應(yīng)式數(shù)據(jù)的核心api,這個api返回的是一個proxy,對上面所有屬性的訪問都會被劫持,從而在get的時候收集依賴(也就是正在運行的effect),在set的時候觸發(fā)更新。
ref
對于簡單數(shù)據(jù)類型比如number,我們不可能像這樣去做:
let?data?=?reactive(2)//??oops
data?=?5
這是不符合響應(yīng)式的攔截規(guī)則的,沒有辦法能攔截到data本身的改變,只能攔截到data身上的屬性的改變,所以有了ref。
const?data?=?ref(2)//??ok
data.value=?5
computed
計算屬性,依賴值更新以后,它的值也會隨之自動更新。其實computed內(nèi)部也是一個effect。
擁有在computed中觀察另一個computed數(shù)據(jù)、effect觀察computed改變之類的高級特性。
實現(xiàn)
從這幾個核心api來看,只要effect能接入到React系統(tǒng)中,那么其他的api都沒什么問題,因為它們只是去收集effect的依賴,去通知effect觸發(fā)更新。
effect接受的是一個函數(shù),而且effect還支持通過傳入schedule參數(shù)來自定義依賴更新的時候需要觸發(fā)什么函數(shù),如果我們把這個schedule替換成對應(yīng)組件的更新呢?要知道在hook的世界中,實現(xiàn)當(dāng)前組件強制更新可是很簡單的:
useForceUpdate
export?const?useForceUpdate?=?()?=>?{??const?[,?forceUpdate]?=?useReducer(s?=>?s?+?1,?0);
??return?forceUpdate;
};
這是一個很經(jīng)典的自定義hook,通過不斷的把狀態(tài)+1來強行讓組件渲染。
而rxv的核心api: useStore接受的也是一個函數(shù)selector,它會讓用戶自己選擇在組件中需要訪問的數(shù)據(jù)。
那么思路就顯而易見了:
這樣不就實現(xiàn)了數(shù)據(jù)變化,組件自動更新嗎?
簡單的看一下核心實現(xiàn)
useStore和Provider
import?React,?{?useContext?}?from?'react';import?{?useForceUpdate,?useEffection?}?from?'./share';
type?Selector?=?(store:?T)?=>?S;const?StoreContext?=?React.createContext(null);const?useStoreContext?=?()?=>?{const?contextValue?=?useContext(StoreContext);if?(!contextValue)?{throw?new?Error('could?not?find?store?context?value;?please?ensure?the?component?is?wrapped?in?a?',
????);
??}return?contextValue;
};/**
?*?在組件中讀取全局狀態(tài)
?*?需要通過傳入的函數(shù)收集依賴
?*/export?const?useStore?=?(selector:?Selector):?S?=>?{
??const?forceUpdate?=?useForceUpdate();
??const?store?=?useStoreContext();
??const?effection?=?useEffection(()?=>?selector(store),?{
????scheduler:?forceUpdate,
????lazy:?true,
??});
??const?value?=?effection();
??return?value;
};
export?const?Provider?=?StoreContext.Provider;
這個option是傳遞給Vue3的effectapi,
scheduler規(guī)定響應(yīng)式數(shù)據(jù)更新以后應(yīng)該做什么操作,這里我們使用forceUpdate去讓組件重新渲染。
lazy表示延遲執(zhí)行,后面我們手動調(diào)用effection來執(zhí)行
{
??scheduler:?forceUpdate,
??lazy:?true,
}
再來看下useEffection和useForceUpdate
import?{?useEffect,?useReducer,?useRef?}?from?'react';import?{?effect,?stop,?ReactiveEffect?}?from?'@vue/reactivity';
export?const?useEffection?=?(...effectArgs:?Parameters<typeof?effect>)?=>?{
??//?用一個ref存儲effection
??//?effect函數(shù)只需要初始化執(zhí)行一遍
??const?effectionRef?=?useRef();if?(!effectionRef.current)?{
????effectionRef.current?=?effect(...effectArgs);
??}//?卸載組件后取消effectconst?stopEffect?=?()?=>?{
????stop(effectionRef.current!);
??};
??useEffect(()?=>?stopEffect,?[]);return?effectionRef.current
};export?const?useForceUpdate?=?()?=>?{const?[,?forceUpdate]?=?useReducer(s?=>?s?+?1,?0);return?forceUpdate;
};
也很簡單,就是把傳入的函數(shù)交給effect,并且在組件銷毀的時候停止effect而已。
流程
就簡單的幾行代碼,就實現(xiàn)了在React中使用@vue/reactivity中的所有能力。
優(yōu)點:
缺點:
舉一個例子:
function?Logger()?{??const?logs?=?useStore((store:?Store)?=>?{
????return?store.state.logs.map((log,?idx)?=>?(
??????<p?className="log"?key={idx}>
????????{log}p>
????));
??});
??return?(
????<Card?hoverable><h1>控制臺h1><div?className="logs">{logs}div>Card>
??);
}
這段代碼直接在useStore中返回了整段jsx,是因為map的過程中回去訪問數(shù)組的每一項來收集依賴,只有這樣才能達到響應(yīng)式的目的。
源碼地址
https://github.com/sl1673495/react-composition-api
總結(jié)
以上是生活随笔為你收集整理的react usecontext_Vue3原理实战运用,我用40行代码把他装进了React做状态管理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小森生活收纳箱二级怎么解锁
- 下一篇: asp.net尚未在web服务器上注册_