学习 react-redux store 状态数据管理
实现功能
Provider connect combineReducers createStore
思路
createStore(reducer)
函数 提供getState() dispatch()
combineReducers (reducers)
返回 一个reducer(state={},action)
Provider
返回一个 高阶组件<Provider store={store} />
利用contxet
把storre
传递给子组件connect (mapStateToProps,mapDispatchToProps)(UIComponent)
实现对组件 绑定 状态属性
context 使用
效果图
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {Button} from 'antd'
export default class ComponentA extends Component {
constructor(props){
super(props)
this.state={
colorToB:'red',
colorToC:'pink',
colorToD:'blue'
}
}
// 申明向子组件中传递 contextTypes
static childContextTypes = {
colorToB: PropTypes.string,
colorToC: PropTypes.string,
colorToD: PropTypes.string,
}
// 向子组件传递 指定propTypes 的context 对象
getChildContext(){
return {
colorToB:this.state.colorToB,
colorToC:this.state.colorToC,
colorToD:this.state.colorToD,
}
}
render() {
const {colorToB,colorToC} = this.state
return (
<div style={{background:'#333',padding:20,color:'white'}}>
<p>父组件向子组件传递 context {`{colorToB:${colorToB},colorToC:${colorToC}}`}</p>
<div style={{marginBottom:20}}>
<Button type='primary' onClick={()=> this.setState({colorToB:'green'})}>colorToB: green</Button>
<Button type='danger' onClick={()=> this.setState({colorToB:'red'})}>colorToB: red</Button>
<Button type='primary' onClick={()=> this.setState({colorToC:'green'})}>colorToC: green</Button>
<Button type='danger' onClick={()=> this.setState({colorToC:'red'})}>colorToC: red</Button>
</div>
<ComponentB></ComponentB>
</div>
)
}
}
class ComponentB extends Component{
constructor(props,context){
super(props)
console.log(context)
}
// 申明接收 父组件 contextTypes
static contextTypes={
colorToB:PropTypes.string
}
render(){
return (
<div style={{background:this.context.colorToB,padding:20}}>
<p>子组件B 接收到 A 传递的 context {`{colorToB:${this.context.colorToB}}`}</p>
<ComponentC></ComponentC>
</div>
)
}
}
class ComponentC extends Component {
static contextTypes = {
colorToC: PropTypes.string
}
render() {
const {colorToC} = this.context
return (
<div style={{background:colorToC,padding:20}}>
<p>嵌套子组件C 接收到 A 传递的 context {`{colorToC:${this.context.colorToC}}`}</p>
<ComponentD></ComponentD>
</div>
)
}
}
function ComponentD (){
return (
<div style={{background:'blue',padding:20}}>
<p>嵌套子组件D </p>
</div>
)
}
react-redux 实现效果
核心功能函数 react-redux.js
// 实现 provider connect combineReducers createStore
import PropTypes from 'prop-types'
import React,{Component} from 'react'
// 合并多个 reducer 里的 state {state1,state2}
export function combineReducers(reducers){
// 返回一个 reducer()
return (state={},action) => {
// 遍历 reducers 对象 返回合并的新 state
let newState = Object.keys(reducers).reduce((totalState,key)=>{
totalState[key] = reducers[key](state[key],action)
return totalState
},{})
return newState
}
}
export function createStore(reducer){
// 初始化传入的reducer 得到 state
let state = reducer({},{type:'@@init'})
const listeners = []
function getState(){
return state
}
function dispatch (action) {
// 得到新的 state
const newState = reducer(state,action)
// 替换state
state = newState
// 执行监听
listeners.forEach( listener => listener())
}
function subscritbe (listener){
listeners.push(listener)
}
return {
getState,
dispatch,
subscritbe
}
}
export class Provider extends Component {
// 申明接收一个 store 对象
static propTypes = {
store:PropTypes.object.isRequired
}
// 申明向子节点传递 context
static childContextTypes = {
store:PropTypes.object
}
// 向子节点传递包含store的 context
getChildContext(){
return {store:this.props.store}
}
render(){
// 返回 provider 里 的子节点
return this.props.children
}
}
export function connect(mapStateToProps,mapDispatchToProps){
// 返回一个函数 方法对传入的 ui 组件 包装并绑定 mapStateToProps,mapDispatchToProps 后返回
return (UiComponent) => {
return class wrapperComponent extends Component{
// 申明接收 store
static contextTypes = {
store:PropTypes.object
}
constructor(props,context){
super(props)
const {store} = context
console.log(store)
//数据属性 从store 里读取 需要的 state 保存到 wrapperComonent 状态响应 state 里
this.state = mapStateToProps(store.getState())
// 方法属性
if( typeof mapDispatchToProps === 'function'){
// 当 传入 mapDispatchToProps (dispatch) => {
// return {
// xxx: ()=> dispatch({})
// }
// }
this.actions = mapDispatchToProps(store.dispatch)
}else{
// 当传入 mapDispatchToProps{
// xxx:xxx
// }
this.actions = Object.keys(mapDispatchToProps).reduce((actions,key)=>{
//mapDispatchToProps[key](...arg) 将返回函数 (dispatch)=> dispatch({type:xxx})
actions[key] = (...arg) => mapDispatchToProps[key](...arg)(store.dispatch)
return actions
},{})
// console.log(this.actions)
}
// 监听store state改变后 对wrapperComonent state做相应改变 从而改变 UICompoent 状态
store.subscritbe(()=> {
this.setState({...mapStateToProps(store.getState())})
})
}
render(){
//对ui组件 绑定 mapStateToProps,mapDispatchToProps
return (
<UiComponent {...this.state} {...this.actions}></UiComponent>
)
}
}
}
}
测试使用
index.jsx
import React, { Component } from 'react'
import {Provider} from '../学习/react-redux'
import {store} from './store.js'
import TestRedux from './testredux'
export default class ReactRedux extends Component {
render() {
return (
<Provider store={store}>
<TestRedux></TestRedux>
</Provider>
)
}
}
store.js
import {combineReducers,createStore} from '../学习/react-redux'
const ADD_COUNT = 'add_count'
const LOG = 'log'
// 导出 action
export const addCount = (step=1) => {
return dispatch => {
dispatch({type:ADD_COUNT,step})
dispatch({
type:LOG,
log:JSON.stringify({type:ADD_COUNT,step}),
date: Date.now()
})
}
}
// 计数 reducer
function count (state = 0, action) {
switch (action.type) {
case ADD_COUNT:
console.log(action)
return state+action.step
default:
return state
}
}
// 日志 reducer
function log (state=[{date:1,log:'null'}],action){
switch (action.type) {
case LOG:
return [{date:action.date,log:action.log},...state]
default:
return state
}
}
// 合并reducers
const reducer = combineReducers({count,log})
// 导出store
export const store = createStore(reducer)
testRedux.jsx
import React, { Component } from 'react'
import {Button,Icon} from 'antd'
import {connect} from '../学习/react-redux'
import {addCount} from './store.js'
class TestRedux extends Component {
updateCount = (step) => {
this.props.addCount(step)
}
formatDate(times){
const t = new Date(times)
return `${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} -- ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`
}
render() {
const {count,log} = this.props
return (
<div style={{background:'#ccc',padding:20}}>
<p>测试使用自己实现的 react-redux</p>
<p>count: {count}</p>
<Button type='primary' onClick={()=> this.updateCount(2)}><Icon type='plus'></Icon></Button>
<Button type='primary' onClick={()=> this.updateCount(-2) }><Icon type='minus'></Icon></Button>
<div>
<p>日志打印</p>
{ log.map( item => <p key={item.date}>{ this.formatDate(item.date)}---------{item.log}</p>)}
</div>
</div>
)
}
}
// const mapDispatchToProps = (dispatch) => {
// return {
// addCount: () => dispatch({type:'add_count',step:1})
// }
// }
export default connect(
state=>({
count: state.count,
log: state.log
}),
{addCount}
)(TestRedux)