react中使用redux & 原理解读

仅个人理解,有不当的地方还请批评指正。


Redux

store(贮存): 就是存储state的地方

action: 就是一个普通的对象,用来描述更新的type和content。所有数据的变化,都必须通过派发(dipatch)action来更新。相当于action只是一条指令,真正要执行的话需要通过reducer。
例如:const action1 = { type: "ADD_FIEND", info: {name: "lucy", age: 20} }

reducer: 是一个纯函数,将传入的state和action结合起来生成(返回)一个新的state。


举个例子,便于个人理解

store 就好比一家服装加工厂,采购了各种不同的服饰,这个服饰就可以类比成一个个state

服装厂建成了,工厂开始接收订单了,不同的服饰会有不同的订制需求,比如说这家要染色,那家要裁剪,这些不同的订单就可以类比成 action

用什么根据订单进行服装加工呢?->机器!对于染色的订单,需要专门的染色器;对于裁剪的订单,则需要专门的裁剪器,这些机器就可以类比成 reducer。对于染色订单,送过来什么衣服,需要染什么色,则对应reducer中传入的state 和 action 。一个工程可以有多个机器 ,即一个store 可以有多个 reducer(一般来说一个reducer对应一个state)。工厂在创建时就要采购好各种机器,对应store创建时需要传入reducer

下面是一个非常简单的例子:

// state,类比成 需要裁剪的衣服
const initialState = {
	counter: 0
}

// reducer,类比成 裁剪器
function reducer(state = initialState, action){
	switch(action.type){
		case "INCREMENT":
			return {...state, counter: state.counter + 1};
	}
}

// store(创建时需要传入一个reducer)
// 类比成 工厂采购裁剪器
const store = redux.createStore(reducer);

// actions,类比成需要裁剪的订单
const action = { type: "INCREMENT" };
const action2 = { type: "ADD_NUMBER", num: 5 };

// 订阅store的修改
store.subscribe(() => {
	console.log("counter: ", store.getState().counter);
})

// 派发action,类比成工厂接收订单
// 调用dispatch后,内部会执行reducer。
store.dispatch(action);

不加connect

一般来说使用redux时候都会创建一个文件夹,将store相关的流程拆开放进去。分别有:

  • actionCreater.js:定义 action 并导出,
  • constants.js,:定义类型常量的
  • index.js:创建store,并绑定reducer,
  • reducer.js:定义并导出 reducer。

如果在组件内要用到 redux,就需要这样写:

import React, { PureComponent } from 'react';

import store from '../store';
import { 
  subAction
} from "../store/actionCreators";

export default class About extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      counter: store.getState().counter
    }
  }

  componentDidMount() {
    this.unsubscribue = store.subscribe(() => {
      this.setState({
        counter: store.getState().counter
      })
    })
  }

  componentWillUnmount() {
    this.unsubscribue();
  }

  render() {
    return (
      <div>
        <hr/>
        <h1>About</h1>
        <h2>当前计数: {this.state.counter}</h2>
        <button onClick={e => this.decrement()}>-1</button>
        <button onClick={e => this.subNumber(5)}>-5</button>
      </div>
    )
  }

  decrement() {
    store.dispatch(subAction(1));
  }

  subNumber(num) {
    store.dispatch(subAction(num));
  }
}

使用connect抽离公共部分

上述方法是可以优化的,例如constructor,componentDidMount这些都是可以抽离出来的。所以新建一个connect.js,用来接收state和dispatch需要执行的函数,并将它们作为参数传给已定义好的组件,返回新的组件。

connect.js:

import { PureComponent } from "react";

import store from '../store';

export function connect(mapStateToProps, mapDispachToProp) {
    return function enhanceHOC(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())}
                                         {...mapDispachToProp(store.dispatch)}/>
            }
        }
    }
}

组件使用:

import React from 'react';
import { connect } from '../utils/connect';

import { 
  decAction,
  subAction
} from "../store/actionCreators";

function About(props) {
  return (
    <div>
      <hr/>
      <h1>About</h1>
      <h2>当前计数: {props.counter}</h2>
      <button onClick={e => props.decrement()}>-1</button>
      <button onClick={e => props.subNumber(5)}>-5</button>
    </div>
  )
}

const mapStateToProps = state => {
  return {
    counter: state.counter
  }
};
const mapDispatchToProps = dispatch => {
  return {
    decrement: function() {
      dispatch(decAction());
    },
    subNumber: function(num) {
      dispatch(subAction(num))
    }
  }
};

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

添加 context 进行优化&react-redux

上面就是redux包大体的封装方式,当然了,实际肯定更加复杂,因为上面connect代码依赖了具体业务的store。因此引入 context,在使用的时候将用户的store通过context传过去即可。

react-redux 包提供了这些功能,使用起来也是非常简单:

在根用Provider包裹一下:

import React from 'react';
import ReactDOM from 'react-dom';

import store from './store';

// import { StoreContext } from './utils/context';
import { Provider } from 'react-redux';

import App from './App';

ReactDOM.render(
  // 本质上还是跟context用法一样传的value,只是加了一层封装,传入store
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

组件内使用的时候,将import { connect } from '../utils/connect';改成import { connect } from 'react-redux';即可,其他的和上小部分一样使用。

react-redux 在内部实现时候用到了大量hooks。


更加详细的使用(添加了redux-thunk):

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

store/index.js: 用于创建store

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

// 用于浏览器redux插件
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(reducer, composeEnhancers(
    // 使用中间件thunk,用于在redux中发送异步网路请求
    applyMiddleware(thunk)
));

export default store;

store/reducer.js: 用于将store和action联系,不同的模块单独配置一个reducer,然后合并起来传给index

import { combineReducers } from "redux-immutable";

import { reducer as recommendReducer } from "../pages/discover/c-pages/recommend/store";

const cReducer = combineReducers({
  recommend: recommendReducer,
});

export default cReducer;

App.js中需要用Provider包裹组件,组件就可以通过connect获取store、dispatch等

import { Provider } from "react-redux";
import store from "./store";

export default memo(function App() {
  return (
    <Provider store={store}>
      <HashRouter>
        <SAppHeader />
        {renderRoutes(routes)}
        <SAppFooter />
      </HashRouter>
    </Provider>
  );
});

现在就要创建子模块(以recommend为例)对应的redux,用于向reducer.js中传入recommendReducer。

四个文件分别是actionCreator.js,constants.js,index.js,reducer.js。

constants.js:

export const CHANGE_TOP_BANNERS = "recommend/CHANGE_TOP_BANNERS";
export const CHANGE_HOT_RECOMMEND = "recommend/CHANGE_HOT_RECOMMEND";
....

reducer.js:

import * as actionTypes from "./constants";

const defaultState = {
  topBanners: [],
};

function reducer(state = defaultState, action) {
  switch (action.type) {
    case actionTypes.CHANGE_TOP_BANNERS:
      return state.set("topBanners", action.topBanners);
	case .....
    default:
      return state;
  }
}

export default reducer;

index.js:

import reducer from './reducer';

export {
  reducer
}

actionCreator.js:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值