react-hooks-redux
react-hooks 是 react 官方新的編寫推薦,我們很容易在官方的 useReducer 鉤子上進行一層很簡單的封裝以達到和以往 react-redux \ redux-thunk \ redux-logger 類似的功能,並且大幅度簡化了宣告。
安裝
安裝 react-hooks-redux, 需要 react 版本 >= 16.7
yarn add react-hooks-redux
使用
我們用了不到 35 行程式碼就聲明瞭一個完整的 react-redux 的例子, 擁抱 hooks。
import React from 'react'; import ReactHookRedux from 'react-hooks-redux'; // 通過 ReactHookRedux 獲得 Provider 元件和一個 sotre 物件 const { Provider, store } = ReactHookRedux({ isDev: true, // 列印日誌 initialState: { name: 'dog', age: 0 }, }); function actionOfAdd() { return { type: 'add the count', reducer(state) { return { ...state, age: state.age + 1 }; // 每次需要返回一個新的 state }, }; } function Button() { function handleAdd() { store.dispatch(actionOfAdd()); //dispatch } return <button onClick={handleAdd}>add</button>; } function Page() { const state = store.useContext(); return ( <div> {state.age} <Button />{' '} </div> ); } export default function App() { return ( <Provider> <Page /> </Provider> ); }
總結一下:
準備工作
使用 ReactHookRedux 建立 Provider 元件 和 store 物件
使用 Provide r 包裹根元件
使用
在需要使用狀態的地方 使用 store.useContext() 獲取 store 中的 state
使用 store.dispatch(action()) 派發更新
我們閱讀這個小例子會發現,沒有對元件進行 connect, 沒有編寫 reducer 函式, 這麼簡化設計是為了迎合 hooks, hooks 極大的簡化了我們編寫千篇一律的類模板,但是如果我們還是需要對元件進行 connect, 我們又回到了編寫模板程式碼的老路。
middleware 的編寫
絕大部分情況,你不需要編寫 middleware, 不過它也極其簡單。middleware 是一個一維陣列,陣列中每個物件都是一個函式, 傳入了引數並且如果返回的物件存在, 就會替換成 nextState 並且繼續執行下一個 middleware。
我們可以使用 middleware 進行列印日誌、編寫 chrome 外掛或者二次處理 state 等操作。
我們看看 middleware 的原始碼:
let nextState = reducer(lastState, action); for (let i = 0; i < middleware.length; i++) { const newState = middleware[i](lastState, nextState, action, isDev); if (newState) { nextState = newState; } } return nextState;
效能和注意的事項
效能(和實現上)上最大的區別是,react-hooks-redux 使用 useContext 鉤子代替 connect 高階元件進行 dispatch 的派發。
在傳統的 react-redux 中,如果一個元件被 connect 高階函式進行處理,那麼當 dispatch 時,這個元件相關的 mapStateToProps 函式就會被執行,並且返回新的 props 以啟用元件更新。
而在 hooks 風格中,當一個元件被聲明瞭 useContext() 時,context 相關聯的物件被變更了,這個元件會進行更新。
理論上效能和 react-redux 是一致的,由於 hooks 相對於 class 有著更少的宣告,所以應該會更快一些。
所以,我們有節制的使用 useContext 可以減少一些元件被 dispatch 派發更新。
如果我們需要手動控制減少更新 可以參考 useMemo 鉤子的使用方式進行配合。
以上都是理論分析,由於此庫和此文件是一個深夜的產物,並沒有去做效能上的基準測試,所以有人如果願意非常歡迎幫忙做一些基準測試。
其他例子
非同步 action 的例子
import React from 'react';
import ReactHookRedux, { reducerInAction, devLog } from 'react-hooks-redux';
// 通過 ReactHookRedux 獲得 Provider 元件和一個 sotre 物件
const { Provider, store } = ReactHookRedux({
isDev: true, // default is false
initialState: { count: 0, asyncCount: 0 }, // default is {}
reducer: reducerInAction, // default is reducerInAction 所以可省略
middleware: [devLog], // default is [devLog] 所以可省略
actions: {}, // default is {} 所以可省略
});
// 模擬非同步操作
function timeOutAdd(a) {
return new Promise(cb => setTimeout(() => cb(a + 1), 500));
}
const actions = {
// 如果返回的是一個function,我們會把它當成類似 react-thunk 的處理方式,並且額外增加一個ownState的物件方便獲取state
asyncAdd: () => async (dispatch, ownState) => {
const asyncCount = await timeOutAdd(ownState.asyncCount);
dispatch({
type: 'asyncAdd',
// if use reducerInAction, we can add reducer Function repeat reducer
reducer(state) {
return {
...state,
asyncCount,
};
},
});
},
};
function Item() {
const state = store.useContext();
return <div>async-count: {state.asyncCount}</div>;
}
function Button() {
async function handleAdd() {
// 使用 async dispatch
await store.dispatch(actions.asyncAdd());
}
return <button onClick={handleAdd}>add</button>;
}
export default function App() {
return (
<Provider>
<Item />
<Button />
</Provider>
);
}
使用 immutableJS 配合 hooks 減少重渲染的例子
import React, { useCallback } from 'react';
import ReactHookRedux, { createDevLogFromImmutable } from 'react-hooks-redux';
import { Map } from 'immutable';
const { Provider, store } = ReactHookRedux({
isDev: true, // 列印日誌
initialState: Map({ products: ['iPhone'] }),
// createDevLogFromImmutable,提前宣告getIn物件,可以有效的規避toJS的效能開銷
// 例如 createDevLogFromImmutable('user', ['data', 'local'], 'ui', 'products');
middleware: [createDevLogFromImmutable('products')],
});
function actionAddProduct(product) {
return {
type: 'add the product',
reducer(state) {
return state.update('products', p => {
p.push(product);
return [...p];
});
},
};
}
let num = 0;
function Button() {
function handleAdd() {
num += 1;
store.dispatch(actionAddProduct('iPhone' + num)); //dispatch
}
return <button onClick={handleAdd}>add-product</button>;
}
function Page() {
const state = store.useContext();
// 從immutable獲取物件,如果products未改變,會從堆中獲取而不是重新生成新的陣列
const products = state.get('products');
return useCallback(
<div>
<Button />
{products.map(v => (
<div>{v}</div>
))}
</div>,
[products], // 如果products未發生改變,不會進行進行重渲染
);
}
export default function App() {
return (
<Provider>
<Page />
</Provider>
);
}
作者:水落斜陽
連結:https://www.jianshu.com/p/d3ee3b3cc8b7
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。