Hooks
正式推出也有一年出頭了,總結這一年中,本身使用過的Hooks
數據流方式react
目前接觸過的數據流方案大體是2種方式redux
React
的數據流React
的數據流簡單講來,就是使用官方提供的Hooks
,好比最多見的useState
,這種方式也是咱們用的最多的api
經過useState
能將曾經類組件的state
值拆分爲多個閉包
function App() {
const [state, setState] = useState({ foo: 0 });
const [loading, setLoading] = useState(false);
// ...
}
複製代碼
使用useState
最大的好處是簡潔明瞭,我的以爲有2個缺點app
useState
後很差管理state
值過於複雜,在改變值時的合併是比較難處理的針對這兩個小問題,也有另外一個hooks
解決框架
useReducer
能夠說是官方簡版redux
異步
const reducer = (state, { type, loading }) => {
if (type === "FOO") return { ...state, foo: 1 };
if (type === "SET_LOADING") return { ...state, loading };
return state;
};
function App() {
const [state, dispatch] = useReducer(reducer, { foo: 0, loading: false });
// ...
}
複製代碼
與redux
還有一些小差別的就是react
提倡將初始值賦值在useReudcer
的參數中,而不是reducer
的state
ide
上面2種方式在單組件或者是父子層級組件使用仍是比較方便,若是想像redux
同樣不關乎層級共享數據呢?在Hooks
中也提供了對應的方法工具
這種方式是我在這一年中使用最多的跨組件共享狀態的方式了學習
const Context = createContext({});
const reducer = (state, { type, loading }) => {
if (type === "FOO") return { ...state, foo: 1 };
if (type === "SET_LOADING") return { ...state, loading };
return state;
};
function App() {
const [state, dispatch] = useReducer(reducer, { foo: 0, loading: false });
return (
<Context.Provider value={{ state, dispatch }}> // ...children </Context.Provider> ); } function Foo() { const { state, dispatch } = useContext(Context); // ... } 複製代碼
這種方式能將本身的hooks
跨組件共享狀態了,使用仍是比較方便,惟一的缺點就是本身須要使用createContext
來建立Context
而且掛載Provider
,會多一點步驟,也須要本身管理Context
,這能夠說是純手動
unstated-next
是一個200 字節的狀態管理解決方案
function useCounter(initialState = 0) {
const [count, setCount] = useState(initialState)
return { count, setCount }
}
const Counter = createContainer(useCounter);
function Foo() {
const counter = Counter.useContainer();
// ...
}
function App() {
return (
<Counter.Provider> <Foo /> </Counter.Provider> ) } 複製代碼
unstated-next
的實現使用的全是React
的api
,源碼也短,下面會對他的源碼進行分析
使用unstated-next
讓咱們不須要本身建立和管理Context
了,從純手動切換到了半自動
UmiJS中提供了一個useModel
方法,能夠很方便的將hooks
全局使用,它的默認規則是src/models
下導出的hooks
會做用於全局
// src/models/count.ts
export default () => {
const [count, setCount] = useState(0);
return { count, setCount };
};
//
function App() {
const { count } = useModel('count');
// ...
}
複製代碼
useModel
等因而省去了上面的useContext
和掛載<Prvoider>
的步驟,由框架處理了,在React Developer Tools能夠看到最外層是有一個存了導出hooks
的值的Provider
的,我想他的實現方式應該和unstated-next
相似
從本身建立和管理Context
,掛載Provider
,到本身掛載Provder
,再到只須要寫hooks
邏輯,過程就是手動——半自動——全自動
它的缺點就是範圍侷限了,僅限於UmiJS
框架
僅在使用過程當中我的的總結
優勢
React
,沒有額外的學習過程,簡單易用缺點
數據是一個總體,不能作到精確刷新,一旦改變React
就會自動觸發刷新
function Foo() {
const { state: { foo } } = useContext(Context);
// ...
}
function Bar() {
const { state: { bar } } = useContext(Context);
// ...
}
複製代碼
若是在<Foo>
中調用了dispatch()
對state.foo
進行了更改,<Bar>
也會刷新
不基於React
的數據流實現就是數據存儲不在React
裏,數據改變不會直接觸發組件刷新,而是經過其餘的方式觸發組件從新渲染,我只使用過2種
Redux
+React-Redux
DvaJs
在Hooks
推出後,React-Redux
也更新了Hooks
方法,使用useSelector()
來取得state
const Counter = () => {
const counter = useSelector(state => state.counter)
// ...
}
複製代碼
它的優勢是會精確刷新,不會像Context
同樣致使總體刷新,由於useSelector
的從新渲染是本身控制的,而不是交給React
處理
Redux
+React-Redux
的缺點我想用過的都知道,就是須要管理不少文件
DvaJS
其實沒有提供Hooks
的數據流方式
DvaJS
單獨使用不多,基本是使用UmiJS
,實際在Umi
中有Hooks
的方式去獲取數據
雲謙大佬可能全身心投入UmiJS
開發,已經有很長一段時間沒更新了,可是我以爲做爲一個集成度很是高的優秀數據流管理。
僅僅使用過2個月的我對DvaJS
的總結
優勢
redux-sage
解決異步數據流問題redux
+react-redux
簡單不少,再也不會有文件管理問題reducer
,讓一些數據能夠懶加載在類組件時代,沒法拆分state
,類組件感受就很重,組件級state
多了也很難管理,Context
流行度也不算高,使用<Context.Consumer>
讓組件更重了,後來有了contextType
也沒有useContext
這麼簡潔,因此感受以前流行Redux
也是有緣由的,由於React
自己沒有提供好的狀態管理
Hooks
時代,一切都變得更簡潔,官方提供了useReducer
這樣的簡潔版Redux
,同時組件級的state
也更簡單,使用自定義的Hooks
,讓數據和視圖耦合更低了,useContext
+useReducer
的方案能夠解決大部分須要共享狀態的場景
使用了挺久的React
,我感受不少場景都不會全局共享狀態,我如今作的項目就是後臺管理系統,頁面的數據也不會和別的頁面關聯,我都使用Context
一把梭了,因此我更喜歡輕量級的Hooks
數據流解決方案
有時候感受本身真的變成了搬運工,缺少本身的想法,就很呆,上面提到,我很長一段時間都是用useContext
來共享狀態,每次都是手動擋,前幾天忽然想,爲何本身要作重複的工做,一搜果真有大佬已經寫好了
// Provider傳入的Props
export interface ContainerProviderProps<State = void> {
initialState?: State;
children: React.ReactNode;
}
// createContainer建立的Container類型
export interface Container<Value, State = void> {
Provider: React.ComponentType<ContainerProviderProps<State>>;
useContainer: () => Value;
}
// 建立一個Container
export function createContainer<Value, State = void>(
// 自定義數據的hook
useHook: (initialState?: State) => Value
): Container<Value, State> {
// Context用來傳遞數據
let Context = React.createContext<Value | null>(null);
function Provider(props: ContainerProviderProps<State>) {
// 用初始數據初始化自定義的hook
let value = useHook(props.initialState);
// 將hook的返回值賦值給Provider
return <Context.Provider value={value}>{props.children}</Context.Provider>;
}
// 使用Container,值就是自定義的hook的返回值
function useContainer(): Value {
let value = React.useContext(Context);
if (value === null) {
throw new Error("Component must be wrapped with <Container.Provider>");
}
return value;
}
return { Provider, useContainer };
}
export function useContainer<Value, State = void>(
container: Container<Value, State>
): Value {
return container.useContainer();
}
複製代碼
源碼很簡單,就是一個閉包
做者說 「我相信 React 在狀態管理方面已經很是出色」、「我但願社區放棄像 Redux 這樣的狀態管理庫,並找到使用 React 內置工具鏈的更好方法」,我以爲頗有道理
雖然感受這篇沒有什麼技術含量,可是沒有靈感,也有三週沒有分享文章了
歡迎你們關注個人公衆號~
參考文章:精讀《React Hooks 數據流》