实现一个react-redux
react-redux是配合react库管理数据的一种方式,下面试redux的简单实现。
参考http://huziketang.mangojuice.top/books/react/lesson30
import React, {Component} from 'react' import ReactDOM from 'react-dom' import PropTypes from 'prop-types' import './index.css' /** * connect函数是高阶组件,用于包装纯组件并返回一个组件。使store中的数据可以传入到被包装组件,和响应store中数据的更新 * connect接收两个方法,一个指定被包装组件需要通过props传入的参数数据 * 一个指定被包装组件需要通过props传入的回调方法 * connect方法返回的方法接收被包装的组件 */ const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => { class Connect extends Component { static contextTypes = { store: PropTypes.object }; constructor(){ super() this.state = { allProps: {} } } componentWillMount(){ const {store} = this.context this._updateProps() store.subscribe(() => { this._updateProps() }) } _updateProps(){ const {store} = this.context let stateProps = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {} let dispatchProps = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {} this.setState( { allProps: { ...stateProps, ...dispatchProps, ...this.props } } ) } render(){ return <WrappedComponent {...this.state.allProps}/> } } return Connect; } class Header extends Component{ static propTypes = { themeColor: PropTypes.string.isRequired }; render(){ return ( <h1 style={{color: this.props.themeColor}}>React.js xiaoshu</h1> ) } } const mapStateToPropsHeader = (state) => { return { themeColor: state.themeColor } } let CHeader = connect(mapStateToPropsHeader)(Header) class ThemeSwitch extends Component{ static propTypes = { themeColor: PropTypes.string, onSwitchColor: PropTypes.func } handleSwitchColor(color){ if(this.props.onSwitchColor){ this.props.onSwitchColor(color) } } render(){ return ( <div> <button style={{color: this.props.themeColor}} onClick = {this.handleSwitchColor.bind(this, "red")} >Red</button> <button style={{color: this.props.themeColor}} onClick = {this.handleSwitchColor.bind(this, "blue")} >Blue</button> </div> ) } } const mapStateToPropsThemeSwitch = (state) => { return { themeColor: state.themeColor } } const mapDispatchToPropsThemeSwitch = (dispatch) => { return { onSwitchColor: (color) => { dispatch({ type: 'CHANGE_COLOR', themeColor: color}) } } } let CThemeSwitch = connect(mapStateToPropsThemeSwitch, mapDispatchToPropsThemeSwitch)(ThemeSwitch) class Content extends Component { render () { return ( <div> <p style = {{color: this.props.themeColor}}>React.js 小书</p> <CThemeSwitch /> </div> ) } } const mapStateToPropsContent = (state) => { return { themeColor: state.themeColor, } } let CContent = connect(mapStateToPropsContent)(Content); /** * Procider是store的载体,通过context提供了让所有子组件访问store中数据的能力 */ class Provider extends Component { // propTypes 定义了通过创建组件时传入的参数类型,这个功能是通过导入'prop-types'包提供的 // 如果传入了不是指定类型的参数会报错 static propTypes = { store: PropTypes.object.isRequired, // isRequired 强制此参数必须传入 children: PropTypes.any } // 如果提供了getChildContex方法,则必须提供childContextTypes作为context的声明和验证 static childContextTypes = { store: PropTypes.object } /** * getChildContext方法返回context对象,所有子组件都可以访问context * 子组件想要获取context中的内容,就必须写contextTypes来声明和验证要获取的属性的类型 * static contextTypes = { * themeColor: PropTypes.string * }; */ getChildContext () { return { store: this.props.store } } render () { return ( <div>{this.props.children}</div> ) } } /** * createStroe接收一个producer函数并返回store对象 * store对象提供三个方法,getState dispatch subscribe * getState() 获取储存在store中的数据 * dispatch(action) 修改数据(依据action的type属性修改数据) * subscribe(listener) 订阅事件,当调用store.dispatch时会收到事件 */ function createStore(producer){ let state = null; const listeners = []; const subscribe = (listener) => listeners.push(listener); const getState = () => state; const dispatch = (action) => { state = producer(state, action); listeners.forEach((listener) => listener()); }; dispatch({}); return { getState, dispatch, subscribe, }; } /** * 作为参数传入createStore中的reducer函数 * reducer接收数据和action作为参数并返回修改后的数据 */ function themeProducer(state, action){ if(!state){ return { themeColor: 'red' }; } switch(action.type){ case 'CHANGE_COLOR': return { ...state, themeColor: action.themeColor, }; default: return state; } } const store = createStore(themeProducer); class Index extends Component{ render(){ return ( <div> <CHeader/> <CContent/> </div> ) } } ReactDOM.render( <Provider store={store}> <Index /> </Provider>, document.getElementById('root') );