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 当前状态的代码。

相关文章
相关标签/搜索