一、redux
1.1 从一个计数器开始
- Counter.js
 
import React, { Component } from 'react'import store from '../store'window.__store = storeexport default class Counter extends Component {constructor () {super()this.state = {num: store.getState().counter.num}}componentDidMount () {this.unsub = store.subscribe(() => {this.setState({num: store.getState().counter.num})})}componentWillUnmount () {this.unsub()}render () {store.getState().num = 15return (<div><button onClick={() => store.dispatch({type: 'ADD', amount: 1})}>+</button><span>{this.state.num}</span><button onClick={() => store.dispatch({type: 'MINUS', amount: 1})}>-</button></div>)}}
- 我们用到的 redux 的功能:
 
- getState()
 - createStore()
 - combineReducers()
 - dispatch()
 - subscribe()
 
1.2 实现一个简单的 redux
function createStore (reducer) {let state;// 发布订阅:在数据更新后要执行的事件函数let listeners = []// 初始化 state: 因为 state 初始值是通过 reducer 的第一个参数的默认值,所以为了获取这个默认值,需要让 reducer 执行dispatch({})function getState() {return JSON.parse(JSON.stringify(state))}function dispatch(action) {state = reducer(state, action)listeners.forEach(item => item())}function subscribe(fn) {listeners.push(fn)return () => {listeners = listeners.filter(item => item !== fn)}}return {dispatch,getState,subscribe}}function combineReducers (reducers) {// reducers {counter: counter, todo: todo}// state -> {counter: {num: 1}, todo: {list: [], filter: 'all'}}// counter 初始值 -> {num: 1}// todo 初始值 -> {list: [], filter}return (state = {}, action) => {let obj = {}for (let key in reducers) {obj[key] = reducers[key](state[key], action)}return obj}}export { createStore, combineReducers }
二、 react-redux
我们改写用 react-redux 改写 Counter;
2.1 首先通过 Provider 组件把 store 引入到组件树中;
- index.js
 
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App'import { Provider } from './react-redux'import store from './store'ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
2.2 改造 Counter
import React, { Component } from 'react'import { connect } from '../react-redux'class Counter extends Component {render () {// store.getState().num = 15return (<div><button onClick={() => this.props.add(1)}>+</button><span>{this.props.num}</span><button onClick={() => this.props.minus(1)}>-</button></div>)}}let actions = {add (amount) {return {type: 'ADD',amount}},minus (amount) {return {type: 'MINUS',amount}}}let mapDispatchToProps = (dispatch) => {return {add (amount) {dispatch(actions.add(amount))},minus (amount) {dispatch(actions.minus(amount))}}}export default connect(state => ({...state.counter}), actions)(Counter)
2.3 我们实现一个 react-redux
import React, { Component } from 'react'// 使用 context 将 store 引入到组件树中,使用 context 首先创建 Contextlet StoreContext = React.createContext(null)class Provider extends Component {render() {return <StoreContext.Provider value={this.props.store}>{this.props.children}</StoreContext.Provider>}}let bindActionCreators = (actions, dispatch) => {let obj = {}for (let key in actions) {obj[key] = (...args) => dispatch(actions[key](...args))}return obj}// connect 是一个高阶组件let connect = (mapStateToProps, mapDispatchToProps) => (Component) => {return class HOCProxy extends React.Component {// 使用 Context 访问组件树中通过 Context 共享的 store,需要给子组件定义一个 contextType 的静态属性static contextType = StoreContextconstructor (props, context) {super()// 通过 mapStateToProps 把 store 初始化成当前组件的状态// 为什么是私有状态?因为这是一个高阶组件,为了在 store 中数据更新时,我们可以订阅 store 中的状态变更然后更新 state 以期达到更新视图的目的this.state = mapStateToProps(context.getState())}componentDidMount () {// 虽然在子组件中我们省去了订阅 store 更新数据的操作,正是因为这个高阶组件中帮我们订阅了这些状态变更this.unsub = this.context.subscribe(() => {this.setState(mapStateToProps(this.context.getState()))})}componentWillUnmount () {// 当组件即将销毁时不要忘记取消订阅this.unsub()}render () {// 判断 mapDispatchToProps 到底是函数还是 actionCreator 对象,如果是 actionCreator 对象,则我们需要处理一下// 如果是函数,我们直接执行 mapDispatchToProps 获取其返回结果let dispatchToProps = {}if (typeof mapDispatchToProps === 'object') {dispatchToProps = bindActionCreators(mapDispatchToProps, this.context.dispatch)} else {dispatchToProps = mapDispatchToProps(this.context.dispatch)}// 最终返回组件,并且把从 store 中拿来并且放到 state 中的数据以 props 的形式传递给组件,修改 store 中状态也需要做相同的处理return <Component {...this.state} {...dispatchToProps}/>}}}export { Provider, connect }
【发上等愿,结中等缘,享下等福,择高处立,寻平处住,向宽处行】
