最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • TypeScript下使用Hooks的方式重新学习Redux和React-Redux

    正文概述 掘金(沧沧凉凉)   2021-03-14   750

    使用过vuex的同学肯定了解vuex无论是集成难度,还是上手难度,都远小于React-Redux,目前要实现vuex同样的功能则需要3个库,分别是Redux、React-Redux、Redux-Saga,它们分别负责下面几种工作。

    • Redux:Redux是JavaScript状态容器,提供可预测化的状态管理。
    • React-Redux:React官方提供的将React项目与Redux项目绑定的库。
    • Redux-Saga:一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。

    然而由于Redux-Saga的复杂性,本篇文章就先暂且不谈,仅仅谈论一下Redux、React-Redux,也正是因为这一系列的库上手成本比较高,于是由蚂蚁金服推出了一个整合库dva,它整合了这3个库,同时还内置了React-router和fetch,但是从普及程度上面考虑,Redux、React-Redux还是不得不学习一下的。

    那么什么时候该使用Redux,我的想法是:当你觉得组件之间的通信让你头痛的时候,你就可以使用Redux。

    尤其是在一个界面有非常多的表单需要填写,填写完毕后需要将数据统一起来发送给后端,因为你不可能不将表单分割成为一个一个组件,一旦表单组件被分割,里面的数据传递就会非常复杂。当然还有一种解决方式是不分割组件,全部写在一个组件中(我还真见过这种表单组件,没格式化之前4000行代码,一格式化超过1W行,而且几乎没有注释,你根本不知道有些代码是在干什么,直接无法维护...)。

    和Vue一样,在一个比较大的项目中或者一个通信比较复杂的项目中,要实现兄弟组件、爷孙组件的通信是一件非常麻烦和困难的事情(虽然我最近了解到React中有Context),为了解决这个问题,React项目中就引入了ReduxReact-Redux,值得注意的是,Redux不光可以使用在React项目中,在其它框架的项目中也是可以进行使用。

    我们先来尝试一下Redux:

    1. 使用Redux

    Redux的使用一共分为下面几个步骤:

    1. 创建state。
    2. 创建action。
    3. 通过dispatch进行通信。

    当然这是我总结的感觉语言并不官方...下面就来分别看一下这3个步骤吧!

    1.1 创建state

    因为我特别喜欢TypeScript,所以下面的代码都是使用的TypeScript。

    interface action {
      type: "INCREMENT" | "DECREMENT";
    }
    
    const counter = (state = 0, action: action) => {
      switch (action.type) {
        case "INCREMENT":
          return state + 1;
        case "DECREMENT":
          return state - 1;
        default: // 不要忘记默认返回值,不然再创建时会报错
          return state;
      }
    };
    

    1.2 创建Store

    const store = createStore(counter);
    
    store.subscribe(() => console.log(store.getState())); // 当store里面的属性改变时,触发的回调
    

    一般来讲是不需要store.subscribe(() => console.log(store.getState()));这条语句的,但是为了下面我们进行通信的时候能够清楚的看到store中的变化,所以我们添加上这条语句。

    1.3 通信

    store.dispatch({ type: "DECREMENT" }); // 触发state + 1
    store.dispatch({ type: "INCREMENT" }); // 触发state - 1
    

    其中可以将{ type: "DECREMENT" }定义为一个函数。

    const increment = (): action => ({
      type: "INCREMENT",
    });
    
    const decrement = (): action => ({
      type: "DECREMENT",
    });
    
    store.dispatch(increment());
    store.dispatch(decrement());
    

    对于字符串"INCREMENT",推荐定义为一个常量,因为毕竟{ type: "INCREMENT" }中的"INCREMENT"字符串书写的时候没有提示,非常容易写错,不过用了TypeScript的话是会有提示的,这也是TypeScript比较赞的一点。

    如果一旦写错,在TypeScript中还会报错:

    TypeScript下使用Hooks的方式重新学习Redux和React-Redux

    所以在JavaScript中因为"INCREMENT"这一类的字符串书写没有提示的关系,所以很多人会为了防止写错,而给它定义一个常量(当然TypeScript中也推荐这样做)。

    const INCREMENT_TYPE = "INCREMENT";  // 定义常量
    
    const increment = (): action => ({
      type: INCREMENT_TYPE, // 使用常量进行调用
    });
    
    store.dispatch(increment());
    

    一旦定义常量后,就会获得代码提示,也就不是那么容易会写错。

    1.4 传值

    store.dispatch()还可以传入一个参数,一般我们定义参数名称为payload或者data

    interface action {
      type: "INCREMENT" | "DECREMENT";
      payload?: number;    // 传入参数一般命名为 payload 或者 data
    }
    
    const INCREMENT_TYPE = "INCREMENT";
    
    const increment = (nr: number): action => ({
      type: INCREMENT_TYPE,
      payload: nr,
    });
    
    const decrement = (): action => ({
      type: "DECREMENT",
    });
    
    const counter = (state = 0, action: action) => {
      switch (action.type) {
        case "INCREMENT":
          return state + (action.payload as number);
        case "DECREMENT":
          return state - 1;
        default:
          return state;
      }
    };
    
    const store = createStore(counter);
    
    store.subscribe(() => console.log(store.getState()));
    
    store.dispatch(increment(5));
    store.dispatch(decrement());
    

    2. React-Redux

    React Hooks的出现让React的易用性升到了另一个等级,我记得我曾经用React的class组件时,需要进行操作的步骤还挺多,而现在React-Redux提供了两个hook,使用这两个hook就可以轻松的访问Store中的状态和触发其中的行为。

    由于是项目工程文件,肯定是要进行模块化的,虽然上面的那种写法也是可以,但是会给后期维护带来很大的困难,目前我的习惯是分成:

    TypeScript下使用Hooks的方式重新学习Redux和React-Redux

    modules中就放和state相关的东西,比如下面这段代码,就放进modules文件夹下:

    const counter = (state = 0, action: action) => {
      switch (action.type) {
        case "INCREMENT":
          return state + (action.payload as number);
        case "DECREMENT":
          return state - 1;
        default:
          return state;
      }
    };
    

    actions就放触发事件相关的代码,比如下面的这些代码就放入actions文件夹下面的文件中:

    export interface action {
      type: "INCREMENT" | "DECREMENT";
      payload?: number;
    }
    
    const INCREMENT_TYPE = "INCREMENT";
    
    export const increment = (nr: number): action => ({
      type: INCREMENT_TYPE,
      payload: nr,
    });
    
    export const decrement = (): action => ({
      type: "DECREMENT",
    });
    

    而store下的index.ts文件就将上面所有的文件整合成为一个Store,并且导出,同时还可以添加Redux调试工具,关于Redux调试工具具体信息可以点击查看:

    import { combineReducers, createStore } from "redux";
    import counter from "./modules/counter";
    import isLogged from "./modules/isLogged";
    
    // 整合
    const allReducers = combineReducers({ counter, isLogged });
    
    // 注册
    const store = createStore(
      allReducers,
      // @ts-ignore
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // 引入Redux调试工具
    );
    
    // 导出
    export default store;
    

    最后一步就是我们需要引入并且在项目中使用React-Redux。

    import { Provider } from "React-Redux";
    import store from "./store";
    
    ReactDOM.render(
      <Provider store={store}>
        <React.StrictMode>
          <App />
        </React.StrictMode>
      </Provider>,
      document.getElementById("root")
    );
    

    上面的Provider是一个顶层组件,通过它就可以注册一个所有组件都可以访问的store。

    到这里为止,我们已经在React项目中注册了store,接下来我们可以随时在需要的时候在组件中访问store中的数据。

    需要使用到两个hook,其它的Hook可以参考文章最后的参考链接:

    • useSelector:从Redux存储状态中提取数据。
    • useDispatch:该hook会返回一个dispatch函数,通过dispatch函数就可以触发actions。

    2.1 在组件中调用

    知道上面的那两个hook后就可以开始搞事情了。

    import React from "react";
    import "./App.css";
    import { useDispatch, useSelector } from "react-redux";
    import { increment } from "./store/actions";
    
    interface AllState {
      counter: number;
      isLogged: boolean;
    }
    
    function App() {
      // useSelector声明了一个泛型,第一个是state的类型,第二表示返回值的类型
      // 即下面的代码counter的类型是number
      const counter = useSelector<AllState, AllState["counter"]>(
        (state) => state.counter
      );
      const isLogged = useSelector<AllState, AllState["isLogged"]>(
        (state) => state.isLogged
      );
      // 调用useDispatch函数会返回一个dispatch函数
      const dispatch = useDispatch();
      return (
        <div className="App">
          {counter}
          {isLogged ? "1" : "2"}
          <button onClick={() => dispatch(increment(5))}>点我</button>
        </div>
      );
    }
    
    export default App;
    

    因为方便代码演示,所以接口AllState直接在组件中声明,在一般情况下还是推荐声明在index.ts中。

    2.2 解决type重复

    如果你的项目够大,你就会拥有很多的action,那么action type就有可能会出现重复的情况,这就会造成一些BUG的产生,我所知道的解决的方法有两种:

    一种是引入命名空间,即在字符串前面加上xxx/这种写法,vuex就是使用的这种方法。

    export interface action {
      type: "a/INCREMENT";
      payload?: number;
    }
    
    export const INCREMENT_TYPE = "a/INCREMENT"; // 引入命名空间
    
    export const increment = (nr: number): action => ({
      type: INCREMENT_TYPE,
      payload: nr,
    });
    

    一种是使用symbol:

    export interface action {
      type: symbol;
      payload?: number;
    }
    
    export const INCREMENT_TYPE = Symbol("INCREMENT");
    
    export const increment = (nr: number): action => ({
      type: INCREMENT_TYPE,
      payload: nr,
    });
    

    这两种方法都可以解决type有可能重复的问题。

    3. 最后

    到目前为止,你就可以愉快的在项目中使用Redux了,但是还存在一个缺陷,就是无法处理异步请求,如果需要处理异步请求则需要使用Redux-Saga或者Redux-thunk。

    这篇文章所讲述的内容其实都非常基础,但是我看到Redux、React-Redux中还有其它的一些功能,因为我暂且没有使用到所以就暂时不进行讲解了~~(其实是我不会)~~,顺便说一下,更新了hooks的React-Redux是真的非常好用,因为我最开始学习React的时候教程中还是使用的class,当时就觉得对比vuex来说有非常多的不方便的地方。

    参考资料:

    React-Redux Hooks


    起源地下载网 » TypeScript下使用Hooks的方式重新学习Redux和React-Redux

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元