zoukankan      html  css  js  c++  java
  • 从零开始,构建自己的react项目(三)增加redux

    添加redux拢共分几步?

    • 拢共分四步
      1. 新建action
      2. 新建reducer
      3. 新建store
      4. 使用provider包裹

    新建action

    • 通常来说,action需要返回一个通用的格式的数据,可以根据自己业务体量的不同,定义不同的数据结构,我这边是这样定义的
      // 同步的action
      const test = (option) => {
        const { data } = option;
        return {
          type: 'TEST',
          payload: {
            data  
          }
        }
      }
      // 异步的action,通常来说,异步的action一般是伴随着ajax请求的,所以考虑在store创建的时候添加一个promise中间件来专门处理异步的action
      const testAsync = (option) => {
        const { params, success, error, data } = option;
        return {
          type: 'TEST_ASYNC',
          payload: {
            promise: axios({ type: 'post', params, data }), // api层可以根据自己的业务进行一层封装,这个地方就举个例子,可以在这里地方把params和data传入axios,也可以在中间件中区处理
            success,
            error
          }
        } 
      }
    

    新建reducer

    • 官网上的reducer是通过switch来进行判断的,感觉这样的写法,每次要写分支,而且还不能漏掉一个break,一旦漏掉一个break,就会出现很奇怪的错误。所以我将reducer进行一层简单的包裹
    /**
     * 对所有的reducer做一层封装
     * @param {初始化的state}} initialState 
     * @param {初始化的调用action的方法} handlers 
     */
    const createReducer = (initialState, handlers) => {
    
      return (state, action) => {
        let _state = initialState;
    
        if (state !== undefined) {
          _state = state;
        }
    
        const handler = action && action.type ? handlers[action.type] : undefined;
    
        if (!handler) {
          return _state;
        }
    
        return handler(_state, action);
      };
    
    };
    
    /...reducer.../
    const initData = {
      count: 0, // 计数器
    }
    
    const handle = {
      'TEST_ADD': (state, action) => ({ ...state, count: state.count += action.payload.data }),
    }
    
    export default createReducer(initData, handle);
    

    创建store

    • 通常创建store的时候,只需要调用createStore就可以了,但是,针对一些特殊的业务需求,或者是需要一些定制化的功能,可以考虑在创建store的时候添加中间件,来做一些相关的处理
    import { createStore, combineReducers, applyMiddleware } from "redux";
    
    import index from 'Reducers';
    import { promiseMiddleware, LoggerMiddleware } from 'Utils/reduxUtil';
    
    // 混合多个reducer,当有多个时候,直接使用这个方法进行混合
    const reducers = combineReducers({
      index
    });
    
    const store = createStore(
      reducers, 
      applyMiddleware(
        promiseMiddleware, 
        LoggerMiddleware
      )
    );
    
    export default store;
    
    /...中间件,写的比较简单,可以自己根据自己需要去编写.../
    /**
     * 自定义promise中间件,对异步的action作处理,action统一只传type和payload两个值,payload为action的除了type的所有参数集合
     * 需要后端返回的数据格式保持一致,统一为{ data, status, msg },
     * 需要返回一个带getMe的json,getMe为此次promise请求的结果,
     * 如果返回的数据不是data也可以,在配置action时传一个customField的字段告诉中间件其返回的字段名称
     * 同时可以不设置type,直接把action当做一个普通的api请求,保持整体的调用api都是通过action来调用,风格一致。
     */
    const promiseMiddleware = store => next => action => {
      const { payload: { params, promise, success, error, customField } } = action;
      if (!promise) {
        return next(action);
      }
    
      return promise(params).then((res) => {
        action.payload.data = res;
        success && success(res);
        next(action);
      }).catch(err => {
        error && error(err);
      });
    }
    
    const LoggerMiddleware = store => next => action => {
      const { type, payload } = action;
      console.log('-------------------------------------');
      console.log(`type = ${type}`);
      console.log(`payload = `, payload);
      return next(action);
    };
    

    最后,渲染时在最外层使用Provider去包裹,便可以直接使用了.

    • 没啥好注意的,只要前面的没啥问题,到这一步就是加行代码
    import React from "react";
    import ReactDOM from "react-dom";
    import { Router } from 'react-router';
    import { Provider } from 'react-redux';
    import { createBrowserHistory } from 'history';
    
    import { renderRoutes } from 'Utils/component';
    import routes from 'Routes';
    import store from 'Store';
    
    console.log(store.getState())
    
    const browserHistory = createBrowserHistory();
    
    function render() {
      ReactDOM.render(
        <Provider store={store}>
          <Router history={browserHistory}>
            {
              renderRoutes(routes)
            }
          </Router>
        </Provider>,
        document.getElementById("root")
      );
    }
    
    render();
    

    在组件里面使用

    • 在此,我一般是用connect结合reduxbindActionCreators来使用的
    import React, { Component } from 'react';
    
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    
    import * as indexAction from 'Actions';
    import { AsyncComponent } from 'Utils/component';
    
    const HelloWorld = AsyncComponent(() => import('Components/test/HelloWorld'));
    
    class Test extends Component {
      
      add = () => {
        const { indexAction: { testAdd } } = this.props;
        testAdd({ data: Number(this.input.value) });
      }
    
      render() {
        const { count } = this.props;
        return (
          <div className="Test">
            <HelloWorld />
            <input type="number" ref={input => this.input = input} />
            <div className="add" onClick={this.add}>增加</div>
            <div className="show">{count}</div>
          </div>
        )
      }
    }
    
    const mapStateToProps = state => {
      const { index } = state;
      return {
        ...index
      };
    }
    
    const mapDispatchToProps = dispatch => {
      return {
        indexAction: bindActionCreators(indexAction, dispatch),
      }
    }
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps,
      null,
      { forwardRef: true }
    )(Test);
    

    小结

    • 至此,所有的步骤已经结束,redux也完整的加入到了框架里。其实,redux还可以和react-router结合起来使用,当有场景需要使用时,推荐使用connected-react-router
    • 一个包含react所有基本功能的空框架也搭建完毕,整体来说,基本功能都是具备了,可能就是需要针对不同业务来进行进一步的设计。主要是为了写下来,以后自己方便复习。
    • 完整的代码示例,可以访问我的github地址,https://github.com/810307015/ReactDemo
    • 后面想写点除了框架以外的东西,或者是框架本身内部的实现。
  • 相关阅读:
    pip install 报错xcrun: error: active developer path ("/Applications/Xcode.app/Contents/Developer") does not exist
    python virtualenv安装并指定python版本
    python安装ldap报错
    linux服务查看安装目录
    python利用paramiko执行服务器命令
    Python subprocess模块
    Django定义全局变量
    uwsgi: invalid option -- 'x'
    将博客搬至CSDN
    jpeg图片格式编解码移植(1)
  • 原文地址:https://www.cnblogs.com/aloneMing/p/13086234.html
Copyright © 2011-2022 走看看