把redux封装起来,更加简单方便的使用

Redux

Redux 是 JavaScript 状态容器,提供可预测化的状态管理

内容目录

  1. 动机
  2. 核心概念
  3. 三大原则
  4. 先前技术
  5. 基础
  6. 示例

Redux?

  • JavaScript单页应用开发日趋复杂 --> state more --> state在什么时候,由于什么原因,如何变化已然不受控制
  • 分开变化和异步 --> React试图在视图层禁止异步和直接操作DOM来解决这个问题 --> React依旧把处理state中数据的问题留给了我们
  • Redux由Flux演变而来,但受Elm的启发,避开了Flux的复杂性
  • 你可能并不需要Redux

核心概念

三大原则

  • 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • State 是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。

先前技术

  • Redux 可以被看作 Flux 的一种实现吗? 是,也可以说 不是。
  1. 和 Flux 一样,Redux 规定,将模型的更新逻辑全部集中于一个特定的层(Flux 里的 store,Redux 里的 reducer)
  2. Flux 和 Redux 都不允许程序直接修改数据,而是用一个叫作 “action” 的普通对象来对更改进行描述。
  3. 而不同于 Flux ,Redux 并没有 dispatcher 的概念(原因是它依赖纯函数来替代事件处理器)。
  4. 和 Flux 的另一个重要区别,是 Redux 设想你永远不会变动你的数据。
  • Elm 是一种函数式编程语言,更新遵循 (state, action) => state 的规则

基础

action:事件响应

reducer:纯函数,数据改变

store:生成真正的store数据树

示例

app.js:


import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route } from 'react-router-dom';
import { asyncComponent } from 'AsyncComponent';
import HeaderMenu from './components/heard';
import SiderMenu from './components/sider';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer';
import './css/main.css';

const demo1 = asyncComponent(() => import(/* webpackChunkName: 'demo1' */ './demo1'));
const demo2 = asyncComponent(() => import(/* webpackChunkName: 'demo2' */ './demo2'));

let store = createStore(reducer, applyMiddleware(thunk));

const router = (
	<Provider store={store}>
		<HashRouter>
			<div>
				<HeaderMenu />
				<div className="ant-layout ant-layout-has-sider layout">
					<SiderMenu />
					<Route exact path="/" component={demo1} />
					<Route exact path="/demo/demo1" component={demo1} />
					<Route exact path="/demo/demo2" component={demo2} />
				</div>
			</div>
		</HashRouter>
	</Provider>
);

ReactDOM.render(router, document.getElementById('content'));

复制代码

index.js:


import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Layout, Breadcrumb, Button } from 'antd';
import 'date-util';
import ModalTip from 'modalTip';
import { changeNeedCode } from './action';

const { Content } = Layout;

/**
 * demo1
 */

const mapStateToProps = (state, ownProps) => {
	return {
		demo1Store: state.demo1Store
	};
};

const mapDispatchToProps = (dispatch, ownProps) => {
	return {
		changeNeedCode: bindActionCreators(changeNeedCode, dispatch)
	};
};

class demo1 extends React.Component {
	componentDidMount() {
		let that = this;
		let changeNeedCode = that.props.changeNeedCode;
		changeNeedCode('zhanghao');
	}

	render() {
		let that = this;

		return (
			<Layout style={{ padding: '0 24px 24px' }}>
				<Breadcrumb style={{ margin: '12px 0' }}>
					<Breadcrumb.Item>demo</Breadcrumb.Item>
					<Breadcrumb.Item>demo1</Breadcrumb.Item>
				</Breadcrumb>
				<Content style={{ background: '#fff', padding: 24, margin: 0, minHeight: 280 }}>
					<div>
						<div>{new Date().format('yyyy-MM-dd hh:mm:ss')}</div>
						<div>{that.props.demo1Store.welcomeText}</div>
						<div>是否需要验证码{that.props.demo1Store.needCode}</div>
						<div>
							<Button onClick={() => that._showModalTip('info')}>Info</Button>
							<Button onClick={() => that._showModalTip('success')}>Success</Button>
							<Button onClick={() => that._showModalTip('error')}>Error</Button>
							<Button onClick={() => that._showModalTip('warning')}>Warning</Button>
						</div>
					</div>
				</Content>
			</Layout>
		);
	}

	_showModalTip(type) {
		switch (type) {
			case 'info':
				ModalTip.infoTip('info');
				break;
			case 'success':
				ModalTip.successTip('success');
				break;
			case 'error':
				ModalTip.errorTip('error');
				break;
			case 'warning':
				ModalTip.warningTip('warning');
				break;
		}
	}
}

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

复制代码

action.js:


import fetch from 'fetch/fetch';
import ModalTip from 'modalTip';

let checkNeedCode = nickName => {
	let params = {};
	params.nickName = nickName;
	params.t = new Date().getTime();

	return fetch
		.post('/oauth/checkLogin', params)
		.then(res => {
			return res.needCode;
		})
		.catch(e => {
			ModalTip.warningTip(e.message);
		});
};

exports.changeNeedCode = nickName => async dispatch => {
	let needCode = await checkNeedCode(nickName);
	dispatch({ type: 'change_needCode', needCode: needCode });
};

复制代码

reducer.js:


import { combineReducers } from 'redux';

const demo1Store = (state = { welcomeText: '', needCode: '' }, action = {}) => {
	switch (action.type) {
		case 'change_welcomeText':
			return { ...state, welcomeText: action.welcomeText };
		case 'change_needCode':
			return { ...state, needCode: action.needCode };
		default:
			return { ...state };
	}
};

const demo2Store = (state = { columnName: '', newsTitle: '', selectText: '选择器1', dUserCode: '' }, action = {}) => {
	switch (action.type) {
		case 'change_columnName':
			return { ...state, columnName: action.columnName };
		case 'change_newsTitle':
			return { ...state, newsTitle: action.newsTitle };
		case 'change_selectText':
			return { ...state, selectText: action.selectText };
		case 'change_dUserCode':
			return { ...state, dUserCode: action.dUserCode };
		default:
			return { ...state };
	}
};

export default combineReducers({
	demo1Store: demo1Store,
	demo2Store: demo2Store
});

复制代码

Reduxm

为了更好的使用Redux,进行二次封装

内容目录

  1. redux存在的问题
  2. 设计思想
  3. api
  4. 使用注意点
  5. 示例

redux存在的问题

  • 一份store树,离开页面再次进入,数据不会初始化
  • reducer拆分造成汇总困难
  • action的type管理混乱,重复问题
  • 繁杂的使用规则,index页面action和store引入,纯函数reducer大量case仅仅为了改变一个值

设计思想(@修饰器)

  • connectstore对ReactDom继承注入action和store,重写componentWillUnmount生命周期,离开页面自动触发store初始化
  • 使用@修饰器、store对reducer提取存入reducerfactory,action对action提取存入actionfactory和actiontypefactory
  • action的type跟随store定义,并隐式添加命名空间解决type重复问题、以及隐式case定义省略大量case

api

    /**
     * 数据注入层
     * 提供createStore、getDevTools、getActionType、getAllInitData四个方法
     *
     * createStore方法,绑定数据到整个react路由层
     * @params router(react路由), debug(是否开启调试工具)
     * @return reactRouter
     *
     * getDevTools方法,获取调试工具视图
     * @return DevTools(调试工具视图)
     *
     * getActionType方法,获取storeName下所有actionType
     * @param storeName(数据层名称)
     * @return {}(storeName下所有actionType)
     *
     * getAllInitData方法,获取storeName下所有初始数据
     * @param storeName(数据层名称)
     * @return {}(storeName下所有初始数据)
     */
    import Store from './store/store';
    /**
     * store修饰器,处理整个store层存入数据工厂
     * @params storeName(数据层名称), allActionType(改变整个数据层的actionType), allStoreLogs(改变整个数据层的打印日志级别)
     * @return true
     */
    const store = Store.store;
    /**
     * storeActionType修饰器,按名称录入actionType
     * @params actionType(数据改变响应type), level(日志级别)
     * @return target
     */
    const storeActionType = Store.storeActionType;
    /**
     * storeDestroy修饰器,按名称录入是否需要销毁
     * @return target
     */
    const storeDestroy = Store.storeDestroy;
    
    /**
     * connectStore修饰器,连接数据,事件和reactDom
     * @params storeList[](页面所需数据层名称), destroyStoreList[](离开页面销毁数据层名称)
     * @return reactDom
     * 由于我会继承你的ReactDom并重写componentWillUnmount生命周期
     * 所以
     * 在你的ReactDom想实现componentWillUnmount生命周期必须加上静态属性
     * 并且上下文还是ReactDom
     * 如下
     * 	static componentWillUnmount (){
         	this._cons();
       	}
    
     _cons(){
            console.log("生命周期销毁");
        }
     */
    import connectStore from './connect/connectstore';
    
    /**
     * 事件注入层
     */
    import Action from './action/action';
    /**
     * action修饰器,处理整个action层存入事件工厂
     * @param actionName(事件层名称)
     * @return target
     */
    const action = Action.action;
    /**
     * actionProps修饰器,按名称录入action
     * @params actionFunName(事件名称), level(日志级别)
     * @return target
     */
    const actionProps = Action.actionProps;
    /**
     * actionInjection修饰器,按名称反向注入事件到reactDom
     * @param actionName(事件名称)
     * @return target
     */
    const actionInjection = Action.actionInjection;
    
    export { Store, store, storeActionType, storeDestroy, connectStore, action, actionProps, actionInjection };
复制代码

使用注意点

  1. 由于我会继承你的ReactDom并重写componentWillUnmout生命周期所以在你的ReactDom想实现componentWillUnmount生命周期必须加上静态属性并且上下文还是ReactDom如下:
static componentWillUnmount (){
         	this._cons();
       	}
    
_cons(){
            console.log("生命周期销毁");
        }
复制代码
  1. 为了方便使用,Store中提供getAllInitData方法,获取storeName下所有初始数据,减少想手动初始化数据时的重复性定义。
  2. (dispatch, _this),action中第二个的系统级入参,提供_this,方便action内部函数互相调用。
  3. app.js路由文件中,如果想使用如下方式:
(r => {
	r.keys().forEach(r);
})(require.context('./', true, /reducer\.js/));
(r => {
	r.keys().forEach(r);
})(require.context('./', true, /action\.js/));
复制代码

来省略手动引入reducer和action的话,所有页面组件必须按如下异步方式引入

const XXX = asyncComponent(() => import(/* webpackChunkName: 'XXX' */ './XXX'));
复制代码

如不想异步引入页面组件,则必须在import { Store } from 'reduxm';和import XXX from '.XXX';之前进行如下引入:

import './XXX/reducer';
import './XXX/action';
复制代码

示例

app.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route } from 'react-router-dom';
import { asyncComponent } from 'AsyncComponent';
import HeaderMenu from './components/heard';
import SiderMenu from './components/sider';
import 'date-util';
import './css/main.css';

(r => {
	r.keys().forEach(r);
})(require.context('./', true, /reducer\.js/));
(r => {
	r.keys().forEach(r);
})(require.context('./', true, /action\.js/));

import { Store } from 'reduxm';

const demo1 = asyncComponent(() => import(/* webpackChunkName: 'demo1' */ './demo1'));
const demo2 = asyncComponent(() => import(/* webpackChunkName: 'demo2' */ './demo2'));

let debug = true;
const router = Store.createStore(
	<HashRouter>
		<div>
			<HeaderMenu />
			<div className="ant-layout ant-layout-has-sider layout">
				<SiderMenu />
				<Route exact path="/" component={demo1} />
				<Route exact path="/demo/demo1" component={demo1} />
				<Route exact path="/demo/demo2" component={demo2} />
				{debug ? Store.getDevTools() : null}
			</div>
		</div>
	</HashRouter>,
	debug
);

ReactDOM.render(router, document.getElementById('content'));
复制代码

index.js:

import React from 'react';
import { Layout, Breadcrumb, Button } from 'antd';
import { connectStore, actionInjection } from 'reduxm';
import ModalTip from 'modalTip';

const { Content } = Layout;

/**
 * demo1
 */

@connectStore(['demo1Store'], ['demo1Store'])
@actionInjection('demo1Action')
export default class demo1 extends React.Component {
	componentDidMount() {
		this.props.changeNeedCode('zhanghao');
		this.props.changeImmutableList(this.props.demo1Store);
	}

	render() {
		let that = this;

		console.log(that.props.demo1Store.immutableList);
		console.log(that.props.demo1Store.immutableList.toJS());

		console.log(that.props.demo1Store.immutableInList.immutableList[0]);
		console.log(that.props.demo1Store.immutableInList.immutableList[0].toJS());

		return (
			<Layout style={{ padding: '0 24px 24px' }}>
				<Breadcrumb style={{ margin: '12px 0' }}>
					<Breadcrumb.Item>demo</Breadcrumb.Item>
					<Breadcrumb.Item>demo1</Breadcrumb.Item>
				</Breadcrumb>
				<Content style={{ background: '#fff', padding: 24, margin: 0, minHeight: 280 }}>
					<div>
						<div>{new Date().format('yyyy-MM-dd hh:mm:ss')}</div>
						<div>{that.props.demo1Store.welcomeText}</div>
						<div>是否需要验证码{that.props.demo1Store.needCode}</div>
						<div>
							<Button onClick={() => that._showModalTip('info')}>Info</Button>
							<Button onClick={() => that._showModalTip('success')}>Success</Button>
							<Button onClick={() => that._showModalTip('error')}>Error</Button>
							<Button onClick={() => that._showModalTip('warning')}>Warning</Button>
						</div>
					</div>
				</Content>
			</Layout>
		);
	}

	_showModalTip(type) {
		switch (type) {
			case 'info':
				ModalTip.infoTip('info');
				break;
			case 'success':
				ModalTip.successTip('success');
				break;
			case 'error':
				ModalTip.errorTip('error');
				break;
			case 'warning':
				ModalTip.warningTip('warning');
				break;
		}
	}
}
复制代码

action.js:

import fetch from 'fetch/fetch';
import ModalTip from 'modalTip';
import { Store, action, actionProps } from 'reduxm';
import immutable from 'immutable';

const demo1Type = Store.getActionType('demo1Store');
const demo1AllInitStore = Store.getAllInitData('demo1Store');

let checkNeedCode = nickName => {
	let params = {};
	params.nickName = nickName;
	params.t = new Date().getTime();

	return fetch
		.post('/oauth/checkLogin', params)
		.then(res => {
			return res.needCode;
		})
		.catch(e => {
			ModalTip.warningTip(e.message);
		});
};

@action('demo1Action')
class demo1Action {
	@actionProps('changeNeedCode')
	static changeNeedCode = nickName => async (dispatch, _this) => {
		let needCode = await checkNeedCode(nickName);
		dispatch({ type: demo1Type.change_needCode, needCode: needCode });
	};

	@actionProps('changeImmutableList', 'error')
	static changeImmutableList = demo1Store => async (dispatch, _this) => {
		let immutableList = demo1Store.immutableList;
		let immutableInList = demo1Store.immutableInList;

		immutableList = immutable.set(immutableList, 0, 4);
		immutableInList.immutableList[0] = immutable.set(immutableInList.immutableList[0], 0, 10);

		dispatch({
			type: demo1Type.change_demo1Store,
			demo1Store: {
				immutableList: immutableList,
				immutableInList: immutableInList
			}
		});

		demo1AllInitStore.welcomeText = 'www---www';
		console.log(demo1AllInitStore);
	};
}
复制代码

reducer.js:

import { store, storeActionType, storeDestroy } from 'reduxm';
import immutable from 'immutable';

@store('demo1Store', 'change_demo1Store')
class demo1 {
	@storeActionType('change_welcomeText')
	@storeDestroy
	static welcomeText = 'Welcome to Redux test!';

	@storeActionType('change_needCode')
	@storeDestroy
	static needCode = 1;

	@storeActionType('change_immutableList', 'waring')
	@storeDestroy
	static immutableList = immutable.fromJS([1, 2, 3]);

	@storeActionType('change_immutableInList')
	@storeDestroy
	static immutableInList = {
		immutableList: [immutable.fromJS([7, 8, 9])]
	};
}
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UmijS 是一个基于 React 的前端开发框架,它提供了一种简单易用的方式来构建单页应用程序。如果你想在 UmiJS 中使用 Redux,可以按照以下步骤进行操作: 1. 安装 Redux 和 React-Redux:在你的项目目录下运行以下命令来安装所需的依赖包: ``` npm install redux react-redux --save ``` 2. 创建 Redux store:在你的项目中创建一个 `store.js` 文件,并使用 Redux 的 `createStore` 方法来创建一个 Redux store。例如: ```javascript import { createStore } from 'redux'; import rootReducer from './reducers'; // 导入你的根reducer const store = createStore(rootReducer); export default store; ``` 3. 创建 reducers:在你的项目中创建一个 `reducers.js` 文件,用于定义你的 reducers。例如: ```javascript // 定义初始状态 const initialState = { // 初始状态数据 }; // 定义 reducer const rootReducer = (state = initialState, action) => { switch (action.type) { // 处理不同的 action 类型,更新 state default: return state; } }; export default rootReducer; ``` 4. 使用 Redux Provider:在你的根组件中使用 Redux 的 `Provider` 组件,将 Redux store 传递给 UmiJS 应用程序。例如: ```javascript import { Provider } from 'react-redux'; import store from './store'; export function rootContainer(container) { return React.createElement(Provider, { store: store }, container); } ``` 5. 在组件中使用 Redux使用 `react-redux` 提供的 `connect` 方法来连接你的组件到 Redux store,并将需要的 state 和 action 传递给组件。例如: ```javascript import { connect } from 'react-redux'; function MyComponent(props) { // 使用 props 中的 state 和 action // ... } const mapStateToProps = state => { // 将需要的 state 映射到组件的 props return { // ... }; }; const mapDispatchToProps = dispatch => { // 将需要的 action 映射

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值