实现一个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')
);

浙公网安备 33010602011771号