动手实现 React-redux(一) connect 和 mapStateToProps
1.结合 context 和 store
import React, { Component } from 'react';
import PropTypes from 'prop-types'
function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}
const themeReducer = (state, action) => {
if (!state) return {
themeColor: 'red'
}
switch (action.type) {
case 'CHANGE_COLOR':
return { ...state, themeColor: action.themeColor }
default:
return state
}
}
const store = createStore(themeReducer)
class Header extends Component {
static contextTypes = {
store: PropTypes.object
}
constructor () {
super()
this.state = { themeColor: '' }
}
componentWillMount () {
const { store } = this.context
this._updateThemeColor()
store.subscribe(() => this._updateThemeColor())
}
_updateThemeColor () {
const { store } = this.context
const state = store.getState()
this.setState({ themeColor: state.themeColor })
}
render () {
return (
<div>
<h1 style={{ color: this.state.themeColor }}>xutongbao</h1>
<ThemeSwitch />
</div>
)
}
}
class ThemeSwitch extends Component {
static contextTypes = {
store: PropTypes.object
}
// dispatch action 去改变颜色
handleSwitchColor (color) {
const { store } = this.context
store.dispatch({
type: 'CHANGE_COLOR',
themeColor: color
})
}
render () {
return (
<div>
<button onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button>
<button onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button>
</div>
)
}
}
class App extends Component {
static childContextTypes = {
store: PropTypes.object
}
getChildContext () {
return { store }
}
render() {
return (
<div>
<Header />
</div>
);
}
}
export default App;
2.connect 和 mapStateToProps
App.js:
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import Header from './Header'
function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}
const themeReducer = (state, action) => {
if (!state) return {
themeColor: 'red'
}
switch (action.type) {
case 'CHANGE_COLOR':
return { ...state, themeColor: action.themeColor }
default:
return state
}
}
const store = createStore(themeReducer)
class App extends Component {
static childContextTypes = {
store: PropTypes.object
}
getChildContext () {
return { store }
}
render() {
return (
<div>
<Header />
</div>
);
}
}
export default App;
Header.js:
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import { connect } from './react-redux'
import ThemeSwitch from './ThemeSwitch'
class Header extends Component {
static propTypes = {
themeColor: PropTypes.string
}
render () {
return (
<div>
<h1 style={{ color: this.props.themeColor }}>xutongbao</h1>
<ThemeSwitch/>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
themeColor: state.themeColor
}
}
Header = connect(mapStateToProps)(Header)
export default Header
react-redux.js:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export const connect = (mapStateToProps) => (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(store.getState(), this.props) // 额外传入 props,让获取数据更加灵活方便
this.setState({
allProps: { // 整合普通的 props 和从 state 生成的 props
...stateProps,
...this.props
}
})
}
render () {
return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}
ThemeSwitch.js
import React, { Component } from 'react';
import PropTypes from 'prop-types'
class ThemeSwitch extends Component {
static contextTypes = {
store: PropTypes.object
}
// dispatch action 去改变颜色
handleSwitchColor (color) {
const { store } = this.context
store.dispatch({
type: 'CHANGE_COLOR',
themeColor: color
})
}
render () {
return (
<div>
<button onClick={this.handleSwitchColor.bind(this, 'red')}>Red</button>
<button onClick={this.handleSwitchColor.bind(this, 'blue')}>Blue</button>
</div>
)
}
}
export default ThemeSwitch