哇塞,有好吃的~

从零开始,构建自己的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
  • 后面想写点除了框架以外的东西,或者是框架本身内部的实现。
posted @ 2020-06-10 16:37  风行者夜色  阅读(142)  评论(0编辑  收藏  举报