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 創(chuàng)建 Provider 組件 和 store 對象
使用 Provide r 包裹根組件

使用

在需要使用狀態(tài)的地方 使用 store.useContext() 獲取 store 中的 state
使用 store.dispatch(action()) 派發(fā)更新

我們閱讀這個小例子會發(fā)現(xiàn),沒有對組件進行 connect, 沒有編寫 reducer 函數(shù), 這么簡化設計是為了迎合 hooks, hooks 極大的簡化了我們編寫千篇一律的類模板,但是如果我們還是需要對組件進行 connect, 我們又回到了編寫模板代碼的老路。

middleware 的編寫

絕大部分情況,你不需要編寫 middleware, 不過它也極其簡單。middleware 是一個一維數(shù)組,數(shù)組中每個對象都是一個函數(shù), 傳入了參數(shù)并且如果返回的對象存在, 就會替換成 nextState 并且繼續(xù)執(zhí)行下一個 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;

性能和注意的事項

性能(和實現(xiàn)上)上最大的區(qū)別是,react-hooks-redux 使用 useContext 鉤子代替 connect 高階組件進行 dispatch 的派發(fā)。

在傳統(tǒng)的 react-redux 中,如果一個組件被 connect 高階函數(shù)進行處理,那么當 dispatch 時,這個組件相關的 mapStateToProps 函數(shù)就會被執(zhí)行,并且返回新的 props 以激活組件更新。

而在 hooks 風格中,當一個組件被聲明了 useContext() 時,context 相關聯(lián)的對象被變更了,這個組件會進行更新。

理論上性能和 react-redux 是一致的,由于 hooks 相對于 class 有著更少的聲明,所以應該會更快一些。

所以,我們有節(jié)制的使用 useContext 可以減少一些組件被 dispatch 派發(fā)更新。

如果我們需要手動控制減少更新 可以參考 useMemo 鉤子的使用方式進行配合。

以上都是理論分析,由于此庫和此文檔是一個深夜的產(chǎn)物,并沒有去做性能上的基準測試,所以有人如果愿意非常歡迎幫忙做一些基準測試。

其他例子

異步 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對象,可以有效的規(guī)避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未改變,會從堆中獲取而不是重新生成新的數(shù)組
  const products = state.get('products');

  return useCallback(
    <div>
      <Button />
      {products.map(v => (
        <div>{v}</div>
      ))}
    </div>,
    [products], // 如果products未發(fā)生改變,不會進行進行重渲染
  );
}

export default function App() {
  return (
    <Provider>
      <Page />
    </Provider>
  );
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 做React需要會什么? react的功能其實很單一,主要負責渲染的功能,現(xiàn)有的框架,比如angular是一個大而...
    蒼都閱讀 14,949評論 1 139
  • 你還在為該使用無狀態(tài)組件(Function)還是有狀態(tài)組件(Class)而煩惱嗎?——擁有了hooks,你再也不需...
    水落斜陽閱讀 82,468評論 11 100
  • 做React需要會什么? react的功能其實很單一,主要負責渲染的功能,現(xiàn)有的框架,比如angular是一個大而...
    李先生Mr_Li閱讀 3,015評論 1 20
  • 月色清涼,有風薄涼。和喜歡的朋友在一起,簡單地切來鹵料,抬箱啤酒,喝開了,興致頗高。有點小醉意,說是因為歡喜,也可...
    雨中葳蕤閱讀 1,274評論 1 6
  • 記不清在哪里看過這句話:“如果可能的話,為你喜歡的歌手寫一篇文章。”所以今天寫文章也不算心血來潮,一直也有此念想...
    J簡單閱讀 276評論 0 0

友情鏈接更多精彩內容