React Hooks 下更愉快的使用 Redux

由於一些團隊性的緣由,各個項目中一直在使用 Redux,所以我也一直忍受着 Redux 開發中的繁瑣。可是自從使用 React Hooks 以及 Redux v7.1.0 推出的 Hooks API 後,減小了很多樣板代碼,也解決了 Redux 在業務開發中缺乏某些功能的缺點。react

組件結合 Redux

在以前的 class 組件中,是利用 HOC 方式來結合 Redux,具體就是 react-redux 的 connect 方法。然而這種方法須要寫不少樣板代碼,由於須要聲明 mapStateToProps 和 mapDispatchToProps。npm

import { connect } from 'react-redux';

const mapStateToProps = state => ({
    counter: state.counter
});
const mapDispatchToProps = dispatch => ({});

connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
複製代碼

由於樣板代碼繁瑣,因此你們很不喜歡對 Container 組件進行拆分,拆分一次可能就須要添加一遍上面的代碼。長此以往,Container 組件就變得愈來愈大,常常看見項目中會有 2000 行代碼的組件。通常 connect 方法的使用會放在組件文件結尾處,一個屏幕放不下那麼多行代碼,查閱 mapStateToProps 和 mapDispatchToProps 就須要在組件文件內跳來跳去。redux

來看看使用 Redux 的 Hooks API 的狀況。後端

import { useSelector, useDispatch } from 'react-redux';

function App() {
    const counter = useSelector(state => state.counter);
    const dispatch = useDispatch();
    
    return (
        <div>{counter}</div>
    );
}
複製代碼

沒有 connect 方法,組件內直接引用 Redux state 數據,更容易進行組件的拆分,於是也不用在組件文件內大幅度轉移目光。數組

Redux state 衍生數據

平常開發中,常常須要使用 Redux state 的衍生數據,例如:對後端返回的列表進行條件過濾。通常狀況下,咱們會寫一個 selector 函數,而後在 mapStateToProps 中引用這個函數。promise

// selectors.js
const listFilter = (list, condition) => {
    // 根據 condition 過濾 list 數據,返回數組
};

// App.js
const mapStateToProps = state => ({
    list: listFilter(state.list, state.condition)
});
複製代碼

一般狀況下,還能夠進行一些緩存操做,防止每次 state 數據變更都引起 listFilter 方法的從新執行,好比使用:reselect緩存

在 React Hooks 中,能夠經過自定義 Hooks 函數來實現相應的功能。bash

import { useMemo } from 'react';
import { useSelector } from 'react-redux';

function useFilteredList() {
    const list = useSelector(state => state.list);
    const condition = useSelector(state => state.condition);
    const getFilteredList = useMemo(() => {
        // listFilter 來自上方代碼
        const filteredList = listFilter(list, condition);
        
        return () => filteredList;
    }, [list, condition]);
    
    return getFilteredList();
}
複製代碼

這樣只須要在組件內直接使用 useFilteredList 就能夠獲取通過條件過濾的列表數據,同時由於 useMemo 的功能,只有在依賴數組中 list, condition 變量變更的時候,listFilter 函數纔會從新執行。異步

另外在使用 useMemo 的時候,下方這樣的狀況是沒法實現緩存的效果的。async

const getFilteredList = useMemo(() => {
    // listFilter 來自上方代碼
    return () => listFilter(list, condition);
}, [list, condition]);
複製代碼

這種狀況下 useMemo 至關於 useCallback,由於返回的是一個計算函數,每次調用 getFilteredList 函數,都會從新執行 listFilter。

反作用處理

Redux 沒有提供配套的反作用的處理方法,須要結合其它庫,致使社區有一堆的解決方案可供選擇,像:redux-thunk、redux-saga、redux-promise等等。其實結合 React Hooks 就能夠很好的處理反作用。

舉個例子:咱們須要根據 id 來獲取這個 id 下的數據,並且須要將數據保存到 Redux store 中。這裏須要使用 react-use 的 useAsync 或 useAsyncFn 方法。

import { useDispatch } from 'react-redux';
import { useAsync } from 'react-use';

function useFetchItem(id) {
    const dispatch = useDispatch();
    
    return useAsync(async () => {
        const response = await ...;
        
        dispatch({
            type: 'xxx',
            response
        });

        return response;
    }, [id]);
};
複製代碼

上面例子中聲明瞭一個自定義 Hooks 函數 useFetchItem,返回一個依賴於 id 變量的反作用函數,只要 id 變更,反作用函數就會從新執行。反作用函數中,使用 dispatch 方法發起了一個 Redux action 來執行相應的 reducer。

useAsync 返回值是關於 async 函數的狀態,它會有如下幾種狀態。

  • 未執行:{ loading: false }
  • 執行中:{ loading: true }
  • 成功:{ loading: false, value: ... }
  • 錯誤:{ loading: false, error: ... }

根據這幾個狀態,能夠完善 UI 的展現,也能夠減小以前在 redux-thunk 或 redux-saga 中一個異步反作用函數中屢次 dispatch 當前狀態的代碼。

相關文章
相關標籤/搜索