1.手写 redux
redux.js
/**
* 手写 redux
*/
export function createStore(reducer) {
// 当前状态
let currentState;
// 监听队列
let currentListeners = [];
// 订阅(观察)
function subscribe(listener) {
// 将listener放入数组
currentListeners.push(listener);
// return 一个方法 取消注册
return function() {
currentListeners = currentListeners.filter(function(l) {
return l !== listener;
})
}
}
// 分发
function dispatch(action) { // 接收 action 对象
/**
* 处理完得到的结果,给当前的状态
* 执行外部传入的 render 方法
*/
currentState = reducer(currentState, action);
/**
* 将订阅者方法 挨个执行一次
*/
currentListeners.forEach((v) => v())
}
// 获取状态值
function getState() {
// 返回当前值
return currentState;
}
// 返回的是个 store 对象
return {subscribe, dispatch, getState}
}
/**
* 用于 react-redux 调用
*/
export function bindActionCreator(creator, dispatch) {
// 包装一下
return (...args) => {dispatch(creator(...args))}
}
export function bindActionCreators(creators, dispatch) {
let bound = {};
Object.keys(creators).forEach(v => {
let creator = creators[v];
bound[v] = bindActionCreator(creator, dispatch);
})
return bound;
}
2.手写 react-redux
myReactRedux.js
/**
* 手写 react-redux
* Provider -> context 技术。可以跨级来使用属性。
*/
import React, { Component } from 'react';
// 类型检查
import PropTypes from 'prop-types';
// 引入手写的redux
import { bindActionCreators } from './redux';
// connect 方法 -- 用于获取数据
export function connect(mapStateToProps=state=>state, mapDispatchToProps={}) { // 高阶组件(方法) -- 去状态的组件
/**
* mapStateToProps 把 状态 转成 属性
* mapDispatchToProps 把 action方法 转成 属性
* react 性能优化最主要的一点是减少状态的使用,将值转为this.props的属性值来代替
*/
// 柯里化
return function(OldComponent) {
return class NewComponent extends Component {
static contextTypes = {
store: PropTypes.object // 类型与外部相同
}
// 构造函数
constructor(props, context) {
super(props, context);
this.state = { // 定义内部状态
props: {
...props // 拆分符
}
};
}
// 生命周期 -- 首次加载之后调用
componentDidMount(){
// 添加监听
const store = this.context.store;
store.subscribe(() => this.update());
this.update();
}
update(){
const store = this.context.store;
const stateProps = mapStateToProps(store.getState()); // 把store中的数据,变为react组件的props属性
// 把原有的 action 加工, 返回 dispatch(action)
const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch);
this.setState({
props: { // 将传入的对象的值作为props的属性
...this.state.props,
...stateProps,
...dispatchProps
} // 类型于对象合并
})
}
render(){
return <OldComponent {...this.state.props} />
}
}
}
}
// /**
// * 上述代码 es6 写法
// * 函数柯里化
// */
// export const connect = (mapStateToProps, mapDispatchToProps) => (OldComponent) => {
// // 接收旧的组件,返回新的组件
// return class NewComponent extends Component {
// //...
// }
// }
// 创建 Provider 组件(类) -- 使得任何内容都可以直接在组件内通过 connect 使用 store
export class Provider extends Component {
// 构造函数
constructor(props, context) { // context 上下文
super(props, context);
this.store = props.store; // 得到传入的store属性
}
/**
* 定义静态对象
* 类型校验 -- 确定是由哪个组件提供的store
*/
static childContextTypes = {
store: PropTypes.object
};
// 获取 child 上下文
getChildContext(){
// 向外提供,便于Provider所有子组件调用
return {store: this.store}
}
// 直接渲染Provider的子组件
render() {
return this.props.children;
}
}
3.测试
demo.js
/**
* 引入 redux
* reducer 和 action 会写在一个文件中
*/
// import { createStore } from 'redux';
import { createStore } from './redux';
// reducer
function inputChange(state = 10, action) {
switch(action.type){
case 'add': // 加
return state + 1;
case 'sub': // 减
return state - 1;
default: // 默认
return state;
}
}
// 定义store
const store = createStore(inputChange);
// 通过 subscribe 订阅数据流
store.subscribe(listener); // 23中设计模式 -- 观察者模式
function listener(argument) {
const current = store.getState();
console.log(`当前数据:${current}`);
}
console.log(store.getState()); // 10
store.dispatch({type: 'add'}); // 11
store.dispatch({type: 'add'}); // 12
store.dispatch({type: 'sub'}); // 11
/**
* 隐式转换 -- 优先级 -- 转换顺序
* Object 高级数据结构 可以转换成 String/Boolean/Number 初级数据结构
*/
console.log({}.length); // undefined
console.log(({}+{}).length); // 30 -- string 优先级高于 number 所以变成字符串相加 -- [object Object] [object Object] 30
.