Redux基础使用


title: Redux基础使用
order: 3


文章目录

    • 一.基础用法
      • 1.简单例子
      • 2.手写connect
      • 3.store的context处理
      • 4.react-redux使用
    • 二.中间件的使用
      • 1.redux中异步请求
      • 2.redux-thunk使用
      • 3.redux-devtools
      • 4.redux-saga
      • 5.中间件的原理
    • 三.reducer拆分
      • 1.拆分reducer
      • 2.combineReducers合并
    • 四.ImmutableJS
      • 1.认识ImmutableJS
      • 2.ImutableJS常见API
      • 3.dva了解
    • 五.redux代码片段
      • 1.`redux`
      • 2.`rxconst`
      • 3.`rxaction`
      • 4.`rxreducer`
      • 5.`rcredux`
      • 6.`rfcredux`
      • 7.`hocredux`
      • 8.`stredux`
      • 9.`comredux`

一.基础用法

安装:yarn add redux react-redux redux-thunk

1.简单例子

index.ts

import {
     createStore} from 'redux';
import reducer from './reducer'
const store = createStore(reducer);
export default store;

actionCreators.ts

import {
      INCREMENT } from "./CONSTANT";
const addAction = (counter:any) => {
     
  return {
     
    type: INCREMENT,
    counter: counter,
  };
};
export {
      addAction };

reducer.ts

import {
      INCREMENT } from "./CONSTANT";
const initialState = {
     
  counter: 20,
};
function reducer(state = initialState, action: any) {
     
  switch (action.type) {
     
    case INCREMENT:
      return {
      ...state, counter: state.counter + action.counter };
    default:
      return state;
  }
}
export default reducer;

constants.ts

export const INCREMENT = 'increment'

在组件里使用

//类组件
import React, {
      PureComponent } from 'react'
import store from './store'
import {
      addAction } from './store/actionCreators'
export default class Test extends PureComponent {
     
    state = {
     
        counter: store.getState().counter
    }
    componentDidMount() {
     
        store.subscribe(() => {
     
            this.setState({
     
                counter: store.getState().counter
            })
        })

    }
    render() {
     
        const {
      counter } = this.state;
        return (
            <div>
                <h1>Test</h1>
                <div>当前计数为:{
     counter}</div>
                <button onClick={
     () => {
      this.increment() }}>+1</button>
                <button onClick={
     () => {
      this.addCounter() }}>+5</button>
                <hr />
            </div>
        )
    }
    increment = () => {
     
        store.dispatch(addAction(1))
    }
    addCounter = () => {
     
        store.dispatch(addAction(5))
    }
}
//函数组件
import React, {
      memo } from 'react';
import {
      useState } from 'react'
import store from '../store'
import {
      addAction } from '../store/actionCreators'

export default memo(function Demo1() {
     

    const initCounter = store.getState().counter;
    const [a, setA] = useState<any>(initCounter);
    const add = () => {
     
        store.dispatch(addAction(1));
        setA(store.getState().counter)
    }
    return (
        <div>
            <div>{
     a}</div>
            <button onClick={
     add}>点我用reducer派发action</button>
        </div>
    )
})

2.手写connect

解决监听store数据改变的代码,都需要在 componentDidMount中完成;

解决派发事件,我们都需要去先拿到 store, 在调用其 dispatch 等;

接受两个参数:

  • 参数一:里面存放 component 希望使用到的 State 属性;
  • 参数二:里面存放 component 希望使用到的 dispatch动作;

有一个返回值、是一个高阶组件:

  • constructor中的state中保存一下我们需要获取的状态;
  • componentDidMount中订阅store中数据的变化,并且执行 setState操作;
  • componentWillUnmount中需要取消订阅;
  • render函数中返回传入的WrappedComponent,并且将所有的状态映射到其props中;
  • 这个高阶组件接受一个组件作为参数,返回一个class组件:
import React, {
      PureComponent } from "react";

import store from '../store';

export default function connect(mapStateToProps, mapDispatchToProps) {
     
  return function handleMapCpn(WrappedComponent) {
     
    return class extends PureComponent {
     
      constructor(props) {
     
        super(props);

        this.state = {
     
          storeState: mapStateToProps(store.getState())
        }
      }

      componentDidMount() {
     
        this.unsubscribe = store.subscribe(() => {
     
          this.setState({
     
            storeState: mapStateToProps(store.getState())
          })
        })
      }

      componentWillUnmount() {
     
        this.unsubscribe();
      }

      render() {
     
        return <WrappedComponent {
     ...this.props} 
                                 {
     ...mapStateToProps(store.getState())}
                                 {
     ...mapDispatchToProps(store.dispatch)}/>
      }
    }
  }
}

在home和props文件中,我们按照自己需要的state、dispatch来进行映射、比如home.js中进行如下修改:

  • mapStateToProps:用于将state映射到一个对象中,对象中包含我们需要的属性;

  • mapDispatchToProps:用于将dispatch映射到对象中,对象中包含在组件中可能操作的函数;

  • 当调用该函数时,本质上其实是调用dispatch(对应的Action);

const mapStateToProps = state => {
     
  return {
     
    counter: state.counter
  }
}

const mapDispatchToProps = dispatch => {
     
  return {
     
    addNumber: function(number) {
     
      dispatch(addAction(number));
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Home);

有了connect函数,我们之后只需要关心从statedispatch中映射自己需要的状态和行为即可;

3.store的context处理

但是上面的connect函数有一个很大的缺陷:依赖导入的store

正确的做法是我们提供一个Provider,Provider来自于我们创建的Context,让用户将store传入到value中即可;

创建一个context.js文件:

import {
      createContext } from 'react';

export const StoreContext = createContext();

修改connect函数中class组件部分的代码:

  • 注意下面我们将class组件的名称明确的定义出来,并且给它的contextType进行了赋值;
  • 在组件内部用到store的地方,统一使用this.context代替(注意:constructor中直接使用第二个参数即可)
import React, {
      PureComponent } from "react";

import {
      StoreContext } from './context';

export default function connect(mapStateToProps, mapDispatchToProps) {
     
  return function handleMapCpn(WrappedComponent) {
     
    class ConnectCpn extends PureComponent {
     
      constructor(props, context) {
     
        super(props);

        this.state = {
     
          storeState: mapStateToProps(context.getState())
        }
      }

      componentDidMount() {
     
        this.unsubscribe = this.context.subscribe(() => {
     
          this.setState({
     
            storeState: mapStateToProps(this.context.getState())
          })
        })
      }

      componentWillUnmount() {
     
        this.unsubscribe();
      }

      render() {
     
        return <WrappedComponent {
     ...this.props}
          {
     ...mapStateToProps(this.context.getState())}
          {
     ...mapDispatchToProps(this.context.dispatch)} />
      }
    }

    ConnectCpn.contextType = StoreContext;

    return ConnectCpn;
  }
}

在入口的index.js中,使用Provider并且提供store即可:

import {
      StoreContext } from './utils/context';
import store from './store';

ReactDOM.render(
  <StoreContext.Provider value={
     store}>
    <App />
  </StoreContext.Provider>,
  document.getElementById('root')
);

4.react-redux使用

使用connect函数:

import React, {
      PureComponent } from 'react';
import {
      connect } from "react-redux";
// import connect from '../utils/connect2';
export default connect(mapStateToProps, mapDispatchToProps)(Home);

使用Provider:

  • 将之前自己创建的ContextProvider,换成react-reduxProvider组件:
  • 注意:这里传入的是store属性,而不是value属性;
import {
      Provider } from 'react-redux';

import store from './store';

ReactDOM.render(
  <Provider store={
     store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

二.中间件的使用

在之前简单的案例中,redux中保存的counter是一个本地定义的数据,我们可以直接通过同步的操作来dispatch action,state就会被立即更新。

但是真实开发中,redux中保存的很多数据可能来自服务器,我们需要进行异步的请求,再将数据保存到redux中。

1.redux中异步请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivTyBDg6-1634980687237)(./images/14.webp)]

redux也引入了中间件(Middleware)的概念:

  • 这个中间件的目的是在dispatch的action最终达到的reducer之间,扩展一些自己的代码;
  • 比如日志记录、调用异步接口、添加代码调试功能等等;

redux-thunk是如何做到让我们可以发送异步的请求呢?

  • 我们知道,默认情况下的dispatch(action),action需要是一个JavaScript的对象;
  • redux-thunk可以让dispatch(action函数),action可以是一个函数
  • 该函数会被调用,并且会传给这个函数一个dispatch函数和getState函数;
  • dispatch函数用于我们之后再次派发action;
  • getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态;

2.redux-thunk使用

//redux引入applyMiddleware
import {
     
    createStore,
    applyMiddleware,
} from 'redux';
// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer
const enhancer = applyMiddleware(thunkMiddleware);
// 将enhancer作为第二个参数传入到createStore中
const store = createStore(reducer, enhancer);

定义返回一个函数的action:

  • 注意:这里不是返回一个对象了,而是一个函数;
  • 该函数在dispatch之后会被执行;
const getHomeMultidataAction = () => {
     
  return (dispatch) => {
     
    axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
     
      const data = res.data.data;
      dispatch(changeBannersAction(data.banner.list));
      dispatch(changeRecommendsAction(data.recommend.list));
    })
  }
}

修改home.js中的代码:

import React, {
      PureComponent } from 'react';
import {
      connect } from "react-redux";

import {
     
  addAction,
  getHomeMultidataAction
} from '../store/actionCreators';

class Home extends PureComponent {
     
  componentDidMount() {
     
    this.props.getHomeMultidata();
  }

  ...其他逻辑代码
}

...mapStatetoProps

const mapDispatchToProps = dispatch => {
     
  return {
     
    addNumber: function(number) {
     
      dispatch(addAction(number));
    },
    getHomeMultidata() {
     
      dispatch(getHomeMultidataAction());
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Home);

3.redux-devtools

import {
      createStore, applyMiddleware, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import reducer from './reducer.js';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer
const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware));
// 将enhancer作为第二个参数传入到createStore中
const store = createStore(reducer, enhancer);

export default store;

trace打开:

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
     trace: true}) || compose;

4.redux-saga

saga中间件使用了ES6的generator语法

数编一个生成器函数:

function *foo() {
     //生成器generator函数
  yield "Hello";
  yield "World";
}

const iterator = foo();//迭代器iterator
console.log(iterator, typeof iterator); // 一个object类型的iterator对象

调用iterator的next函数,会销毁一次迭代器,并且返回一个yield的结果:

// 调用一次next()是消耗一次迭代器
iterator.next(); // {value: "Hello", done: false}
iterator.next(); // {value: "World", done: false}
iterator.next(); // {value: undefined, done: true}

研究一下foo生成器函数代码的执行顺序:

function *foo() {
     
  console.log("111111");
  yield "Hello";
  console.log("222222");
  yield "World";
  console.log("333333");
}

// 调用一次next()是消耗一次迭代器
iterator.next(); // {value: "Hello", done: false}
// 打印111111
iterator.next(); // {value: "World", done: false}
// 打印222222
iterator.next(); // {value: undefined, done: true}
// 打印333333

generatorpromise一起使用:

function *bar() {
     
  const result = yield new Promise((resolve, reject) => {
     
    setTimeout(() => {
     
      resolve("Hello Generator");
      return "Hello";
    }, 2000);
  });
  console.log(result);
}

const bIterator = bar();
bIterator.next().value.then(res => {
     
  bIterator.next(res);
});

集成redux-saga中间件

import {
      createStore, applyMiddleware, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducer.js';
import mySaga from './saga';

// 通过createSagaMiddleware函数来创建saga中间件
const sagaMiddleware = createSagaMiddleware();

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
     trace: true}) || compose;

// 通过applyMiddleware来结合多个Middleware, 返回一个enhancer
const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware, sagaMiddleware));
// 将enhancer作为第二个参数传入到createStore中
const store = createStore(reducer, enhancer);

// 必须启动saga中间件,并且传入其要监听的generator
sagaMiddleware.run(mySaga);

export default store;

saga.js文件的编写:

  • takeEvery:可以传入多个监听的actionType,每一个都可以被执行(对应有一个takeLastest,会取消前面的)
  • put:在saga中派发action不再是通过dispatch,而是通过put;
  • all:可以在yield的时候put多个action;
import {
      takeEvery, put, all } from 'redux-saga/effects';
import axios from 'axios';

import {
     
  FETCH_HOME_MULTIDATA
} from "./constants";
import {
     
  changeBannersAction,
  changeRecommendsAction,
} from './actionCreators';

function* fetchHomeMultidata(action) {
     
  const res = yield axios.get("http://123.207.32.32:8000/home/multidata");
  console.log(res);
  const data = res.data.data;
  yield all([
    put(changeBannersAction(data.banner.list)),
    put(changeRecommendsAction(data.recommend.list))
  ])
}

function* mySaga() {
     
  yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
}

export default mySaga;

5.中间件的原理

打印日志需求

事实上,我们可以利用一个hack一点的技术:Monkey Patching,利用它可以修改原有的程序逻辑;

  • 已经直接修改了dispatch的调用过程;
  • 在调用dispatch的过程中,真正调用的函数其实是dispatchAndLog
let next = store.dispatch;

function dispatchAndLog(action) {
     
  console.log("dispatching:", addAction(10));
  next(addAction(5));
  console.log("新的state:", store.getState());
}

store.dispatch = dispatchAndLog;

可以将它封装到一个模块中,只要调用这个模块中的函数,就可以对store进行这样的处理:

function patchLogging(store) {
     
  let next = store.dispatch;

  function dispatchAndLog(action) {
     
    console.log("dispatching:", action);
    next(addAction(5));
    console.log("新的state:", store.getState());
  }

  store.dispatch = dispatchAndLog;
}

thunk需求

redux中利用一个中间件redux-thunk可以让我们的dispatch不再只是处理对象,并且可以处理函数

dispatch进行转换,这个dispatch会判断传入的

function patchThunk(store) {
     
  let next = store.dispatch;

  function dispatchAndThunk(action) {
     
    if (typeof action === "function") {
     
      action(store.dispatch, store.getState);
    } else {
     
      next(action);
    }
  }

  store.dispatch = dispatchAndThunk;
}

将两个patch应用起来,进行测试:

patchLogging(store);
patchThunk(store);

store.dispatch(addAction(10));

function getData(dispatch) {
     
  setTimeout(() => {
     
    dispatch(subAction(10));
  }, 1000)
}

// 传入函数
store.dispatch(getData);

合并中间件

单个调用某个函数来合并中间件并不是特别的方便,我们可以封装一个函数来实现所有的中间件合并:

function applyMiddleware(store, middlewares) {
     
  middlewares = middlewares.slice();

  middlewares.forEach(middleware => {
     
    store.dispatch = middleware(store);
  })
}

applyMiddleware(store, [patchLogging, patchThunk]);

理解一下上面操作之后,代码的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcgX941B-1634980687242)(./images/16.webp)]

三.reducer拆分

1.拆分reducer

先抽取一个对counter处理的reducer:

// counter相关的状态
const initialCounter = {
     
  counter: 0
}

function counterReducer(state = initialCounter, action) {
     
  switch (action.type) {
     
    case ADD_NUMBER:
      return {
      ...state, counter: state.counter + action.num };
    case SUB_NUMBER:
      return {
      ...state, counter: state.counter - action.num };
    default:
      return state;
  }
}

再抽取一个对home处理的reducer:

// home相关的状态
const initialHome = {
     
  banners: [],
  recommends: []
}

function homeReducer(state = initialHome, action) {
     
  switch (action.type) {
     
    case CHANGE_BANNER:
      return {
      ...state, banners: action.banners };
    case CHANGE_RECOMMEND:
      return {
      ...state, recommends: action.recommends };
    default:
      return state;
  }
}

如果将它们合并起来呢?

const initialState = {
     
}

function reducer(state = initialState, action) {
     
  return {
     
    counterInfo: counterReducer(state.counterInfo, action),
    homeInfo: homeReducer(state.homeInfo, action),
  }
}

reducer文件拆分

已经将不同的状态处理拆分到不同的reducer

对文件结构再次进行拆分:

./store
├── counter
│   ├── actioncreators.js
│   ├── constants.js
│   ├── index.js
│   └── reducer.js
├── home
│   ├── actioncreators.js
│   ├── constants.js
│   ├── index.js
│   └── reducer.js
├── index.js
├── reducer.js
└── saga.js
  • home/actioncreators.js:存放home相关的action;
  • home/constants.js:存放home相关的常量;
  • home/reducer.js:存放分离的reducer代码;
  • index.js:统一对外暴露的内容;

2.combineReducers合并

目前合并的方式是通过每次调用reducer函数自己来返回一个新的对象:

import {
      reducer as counterReducer } from './counter';
import {
      reducer as homeReducer } from './home';

const initialState = {
     
}

function reducer(state = initialState, action) {
     
  return {
     
    counterInfo: counterReducer(state.counterInfo, action),
    homeInfo: homeReducer(state.homeInfo, action),
  }
}

redux提供了一个combineReducers函数可以方便的对多个reducer进行合并:

import {
      combineReducers } from 'redux';

import {
      reducer as counterReducer } from './counter';
import {
      reducer as homeReducer } from './home';

const reducer = combineReducers({
     
  counterInfo: counterReducer,
  homeInfo: homeReducer
})

export default reducer;

combineReducers是如何实现的?

  • 事实上,它也是讲我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);
  • 在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state;
  • 新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;
export default function combineReducers(reducers) {
     
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {
     }
  for (let i = 0; i < reducerKeys.length; i++) {
     
    const key = reducerKeys[i]

    if (process.env.NODE_ENV !== 'production') {
     
      if (typeof reducers[key] === 'undefined') {
     
        warning(`No reducer provided for key "${
       key}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
     
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  // This is used to make sure we don't warn about the same
  // keys multiple times.
  let unexpectedKeyCache
  if (process.env.NODE_ENV !== 'production') {
     
    unexpectedKeyCache = {
     }
  }

  let shapeAssertionError
  try {
     
    assertReducerShape(finalReducers)
  } catch (e) {
     
    shapeAssertionError = e
  }

  return function combination(state = {
      }, action) {
     
    if (shapeAssertionError) {
     
      throw shapeAssertionError
    }

    if (process.env.NODE_ENV !== 'production') {
     
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      )
      if (warningMessage) {
     
        warning(warningMessage)
      }
    }

    let hasChanged = false
    const nextState = {
     }
    for (let i = 0; i < finalReducerKeys.length; i++) {
     
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
     
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}

四.ImmutableJS

数据可变性的问题

const obj = {
     
  name: "why",
  age: 18
}

console.log(obj); // {name: "why", age: 18}
const obj2 = obj;
obj2.name = "kobe";
console.log(obj); // {name: "kobe", age: 18}

有没有办法解决上面的问题呢?

进行对象的拷贝即可:Object.assign或扩展运算符

console.log(obj); // {name: "why", age: 18}
const obj2 = {
     ...obj};
obj2.name = "kobe";
console.log(obj); // {name: "why", age: 18}

1.认识ImmutableJS

为了解决上面的问题,出现了Immutable对象的概念:

  • Immutable对象的特点是只要修改了对象,就会返回一个新的对象,旧的对象不会发生改变;

但是这样的方式就不会浪费内存了吗?

  • 为了节约内存,又出现了一个新的算法:Persistent Data Structure(持久化数据结构或一致性数据结构);

当然,我们一听到持久化第一反应应该是数据被保存到本地或者数据库,但是这里并不是这个含义:

  • 用一种数据结构来保存数据;
  • 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费;

如何做到这一点呢?结构共享:

  • 如下面的静态图;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KYlwYGH-1634980687245)(./images/1.gif)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eXV67Z2L-1634980687251)(./images/17.webp)]

2.ImutableJS常见API

const imjs = Immutable;

// 1.定义JavaScript的Array和转换成immutable的List
const friends = [
  {
      name: "why", age: 18 },
  {
      name: "kobe", age: 30 }
]

// 不会进行深层转换
const imArray1 = imjs.List(friends);
// 会进行深层转换
const imArray2 = imjs.fromJS(friends);
// console.log(imArray1);
// console.log(imArray2);

// 1.定义JavaScript的Object和转换成immutable的Map
const info = {
     
  name: "coderwhy",
  age: 18,
  friend: {
     
    name: "kobe",
    age: 30
  }
}

const imObj1 = imjs.Map(info);
const imObj2 = imjs.fromJS(info);
// console.log(imObj1);
// console.log(imObj2);

// 3.对immutable操作
// 3.1.添加数据
// 产生一个新的immutable对象
console.log(imArray1.push("aaaa"));
console.log(imArray1.set(2, "aaaa"));
// 原来的是不变的
console.log(imArray1);

// 3.2.修改数据
console.log(imArray1.set(1, "aaaa"));
console.log(imArray2.set(2, imjs.fromJS("bbbb")));

// 3.3.删除数据
console.log(imArray1.delete(0).get(0)); // {name: "kobe", age: 30}

// 3.4.查询数据
console.log(imArray1.get(1));
console.log(imArray2.get(1));
console.log(imArray1.get(1).name);
console.log(imArray2.getIn([1, "name"]));

// 4.转换成JavaScript对象
const array = imArray1.toJS();
const obj = imObj1.toJS();
console.log(array);
console.log(obj);

ImmutableJS重构redux

3.dva了解

import React from 'react';
import dva, {
      connect } from 'dva';
import {
      Route } from 'dva/router';

// 1. Initialize
const app = dva();

// 2. Model
app.model({
     
  namespace: 'count',
  state: 0,
  reducers: {
     
    ['count/add'  ](count) {
      return count + 1 },
    ['count/minus'](count) {
      return count - 1 },
  },
});

// 3. View
const App = connect(({
       count }) => ({
     
  count
}))(function(props) {
     
  return (
    <div>
      <h2>{
      props.count }</h2>
      <button key="add" onClick={
     () => {
      props.dispatch({
     type: 'count/add'})}}>+</button>
      <button key="minus" onClick={
     () => {
      props.dispatch({
     type: 'count/minus'})}}>-</button>
    </div>
  );
});

// 4. Router
app.router(
  <Route path="/" component={
     App} />
);

// 5. Start
app.start(document.getElementById('root'));

5 步 4 个接口完成单页应用的编码,不需要配 middleware,不需要初始化 saga runner,不需要 fork, watch saga,不需要创建 store,不需要写 createStore,然后和 Provider 绑定,等等。但却能拥有 redux + redux-saga + … 的所有功能。

五.redux代码片段

1.redux

import {
      connect } from 'react-redux

2.rxconst

export const constantName = 'constantName'

3.rxaction

export const actionName = (payload) => ({
     
    type: type,
    payload
})

4.rxreducer

const initialState = {
     

}

export default (state = initialState, {
       type, payload }) => {
     
    switch (type) {
     

    case typeName:
        return {
      ...state, ...payload }

    default:
        return state
    }
}

5.rcredux

import React, {
      Component } from 'react'
import {
      connect } from 'react-redux'

export class index extends Component {
     
    render() {
     
        return (
            <div>
                
            </div>
        )
    }
}

const mapStateToProps = (state) => ({
     
    
})

const mapDispatchToProps = {
     
    
}

export default connect(mapStateToProps, mapDispatchToProps)(index)

6.rfcredux

import React from 'react'
import {
      connect } from 'react-redux'

export const index = (props) => {
     
    return (
        <div>
            
        </div>
    )
}

const mapStateToProps = (state) => ({
     
    
})

const mapDispatchToProps = {
     
    
}

export default connect(mapStateToProps, mapDispatchToProps)(index)

7.hocredux

import React from 'react'
import PropTypes from 'prop-types'
import {
      connect } from 'react-redux'

export const mapStateToProps = state => ({
     

})

export const mapDispatchToProps = {
     
 
}

export const hocComponentName = (WrappedComponent) => {
     
    const hocComponent = ({
       ...props }) => <WrappedComponent {
     ...props} />

    hocComponent.propTypes = {
     
    }

    return hocComponent
}

export default WrapperComponent => connect(mapStateToProps, mapDispatchToProps)(hocComponentName(WrapperComponent))

自定义redux片段

8.stredux

import {
     
    createStore,
    applyMiddleware,
    compose
} from 'redux';
import thunk from 'redux-thunk'
import reducer from './reducer'

const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)));
export default store;

9.comredux

import {
      combineReducers } from 'redux';

const cReducer = combineReducers({
     

})

export default cReducer;

你可能感兴趣的:(React,react.js,javascript,前端)