我是靠谱客的博主 淡淡帅哥,最近开发中收集的这篇文章主要介绍封装一个在react上更易用的redux框架,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

redux是一个用于管理状态的前端框架,不仅可以用于react,还可以用于vue。它的源码和设计思想,我们在《颤抖吧!一起手写一个redux框架!》已经讲过,没有看过的小伙伴们可以去看一下。

redux的核心是发明了store,通过dispatch来更改store里的值。

可redux要想在react上使用,需要把两者连接起来,这个时候react-redux就出现了。它的主要作用是:

  • componentDidMountstore.subscribe,这样在store.dispatch的时候就能回调subscribelistener
    具体看Provider.js中的源码实现:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ReactReduxContext } from './Context'
class Provider extends Component {
//...
componentDidMount() {
this._isMounted = true
this.subscribe()
}
subscribe() {
const { store } = this.props
this.unsubscribe = store.subscribe(() => {
const newStoreState = store.getState()
if (!this._isMounted) {
return
}
this.setState(providerState => {
//小tip:return null代表不更新state
if (providerState.storeState === newStoreState) {
return null
}
return { storeState: newStoreState }
})
})
// ...
}
}
export default Provider
  • 提供了context,可以实现跨组件通信,而不用去状态提升。
    这其实用到了react自带的api:context
    具体实现是在Provider.js中声明context,并把store.getState()赋值给它。这样其子组件中就可以拿到context值,只不过要写一些模板代码,这些代码都封装在了connect.js中。这也就是为什么我们只要把我们的组件传给高阶组件connect中就可以获取到reduxstate
  • mapStateToProp用来把store中的state传给connect组件,所以在组件中可以通过this.props获取store中state的值。
  • mapDispatchToProps用来把用dispatch包裹的actionCreator传给组件(bindActionCreators中把dispatch给bind到actionCreator上),所以,在组件中通过props可以直接dispatch一个action。

以上大概就是要在react中使用redux要写的代码。
其实,react-redux封装的功能并不一定适合我们的业务场景,相反我们还多写了很多与业务无关的模板代码!
因此,我们可以尝试着自己去封装一个更易用的redux框架。它有如下几个特点:

  • 可以成功地连接react和redux。
  • 不需要在业务代码中手动bindActionCreator,也不需要写mapDispatchToProps和mapStateToProps。
  • 不用写完reducer,再去写action了。使用过react-redux的小伙伴们应该都发现了reducer和action实际上都是为了去更新数据层,却要写两次非常类似的代码。
    它的使用方法也很简单,只需要让你的页面继承这个basePage就可以了。
import React from "react";
import { createStore, applyMiddleware } from "redux";
export default class BasePage {
constructor(props) {
super(props);
this.$actions = null;
this.$store = null;
this.state = {};
}
render() {
let { View } = this;
if (!View) return null;
let pageContext = {
page: this,
state: this.state
};
return <View state={this.state} page={this} />;
}
get store() {
if (this.$store) return this.$store;
this.$store = this.createStore();
return this.$store;
}
get actions() {
if (this.$actions) return this.$actions;
this.store; // trigger createStore
this.$actions = {};
if (!util.isObject(this.model)) return this.$actions;
Object.keys(this.model).forEach(type => {
this.$actions[type] = payload => this.store.dispatch({ type, payload });
});
return this.$actions;
}
createReducer() {
return (state, action) => {
let model = this.model;
let nextState = state;
if (util.isObject(model)) {
let handler = reducer[action.type];
if (util.isFunction(handler)) {
nextState = handler(nextState, action.payload);
}
} else if (util.isFunction(model)) {
nextState = reducer(nextState, action);
}
return nextState;
};
}
createStore() {
const middlewares = [];
if (process.env.NODE_ENV === "development") {
const reduxLogger = require("redux-logger");
const logger = reduxLogger.createLogger();
middlewares.push(logger);
}
const createStoreWithMiddleware = applyMiddleware(...middlewares)(
createStore
);
let store = createStoreWithMiddleware(this.createReducer(), this.state);
store.unsubscribe = store.subscribe(() => {
let nextState = store.getState();
this.setState(nextState);
});
Object.defineProperty(store, "actions", {
get: () => this.actions
});
return store;
}

可以看到,如上框架代码行数并不多,下面我来为大家来讲解一下。
首先,先看render(),在render中给每个view传入了page

<View state={this.state} page={this} />

这主要是用来在组件中调用action来更新state。
例如,我们在CounterView中调用INCRE_BY

export default function CounterView({ state, page }) {
const { count } = state;
return (
<View>
<TouchableOpacity onPress={()=> {
page.actions.INCRE_BY
}}>
<Text style={{ backgroundColor: "red" }}>increase</Text>
</TouchableOpacity>
</View>
);
}

此时页面才开始createStore。。或者说,只有调用页面第一个action来开始createStore。因为,在调用page.actions.INCRE_BY时,实际上会先调用


get actions() {
if (this.$actions) return this.$actions;
this.store; // trigger createStore
this.$actions = {};
if (!util.isObject(this.reducer)) return this.$actions;
Object.keys(this.reducer).forEach(type => {
this.$actions[type] = payload => this.store.dispatch({ type, payload });
});
return this.$actions;
}

此时,this.$actions实际上是空的,所以这个时候就会用this.model来构建action。这里的model不光可以构建action,还可以构建出reducer
我们来看下mode.js是怎样的:

export const INCRE = state => {
let count = state.count + 1;
return {
...state,
count
};
};
export const DECRE = state => {
let count = state.count - 1;
return {
...state,
count
};
};
export const INCRE_BY = (state, aaa = 2) => {
let count = state.count + aaa;
return {
...state,
count
};
};

看到这个model我相信你大致就能猜到我是如何通过model来构建reduceraction的了——构建action时,取出modelkey作为action名,并把每个functiondispatch包裹一下;构建reducer时,就直接调用model中相应keyfunction,注意这里每一个functionreturn一个新的state

而上面在调用action时,当发现store为空时,会接着去createStore

createStore() {
const middlewares = [];
if (process.env.NODE_ENV === "development") {
const reduxLogger = require("redux-logger");
const logger = reduxLogger.createLogger();
middlewares.push(logger);
}
const createStoreWithMiddleware = applyMiddleware(...middlewares)(
createStore
);
let store = createStoreWithMiddleware(this.createReducer(), this.state);
store.unsubscribe = store.subscribe(() => {
let nextState = store.getState();
this.setState(nextState);
});
Object.defineProperty(store, "actions", {
get: () => this.actions
});
return store;
}

以上我们就完成了一个比react-redux更轻量级的redux框架,该框架使得我们能尽可能少地在业务代码中写无脑的模板代码。

最后,该框架是参考自@工业聚,他对于技术的态度,给了我很多前进的动力!

最后

以上就是淡淡帅哥为你收集整理的封装一个在react上更易用的redux框架的全部内容,希望文章能够帮你解决封装一个在react上更易用的redux框架所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(35)

评论列表共有 0 条评论

立即
投稿
返回
顶部