手挽手带你学React:四档(下篇)一步一步学会react-redux

手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中。学完这一章,你就能够开始本身的react项目了。javascript

视频教程

上一篇咱们本身实现了Redux,这一篇咱们来看看如何去实现一个react-reduxhtml

开始以前

本文须要用到的知识java

首先你要会React的基础(这是废话)
高阶组件
React context
知足这三项咱们开始往下看。

react结合redux


搭建基础环境

咱们上一章讲过了redux的原理,内部是有一个store的,store只有dispatch才能够控制它变化。还有在文章一开始咱们讲了React context 能够跨组件传递数据,那么到如今你想到把咱们的store挂到最外层一个组件的context上了吗?话很少说 开始!react

咱们先修改一下咱们的react文件(注意不是redux.html这个文件了)npm

// App.js
import React,{Component} from 'react'
import PropTypes from 'prop-types'  //引入

export default class App extends Component {
    constructor(){
        super()
        this.state={
          
        }
    }
    componentWillMount(){
        // console.log(hashHistory)
    }
    render() {
        return (
            <div>
                <Children />
                <ChildrenTwo />
            </div>
        )
    }
  
}

// 为了展现效果定义子组件一

class Children extends Component{
    constructor(){
        super()
        this.state={
            
        }
    }
    render(){
        return(
            <div>
                <h1>我是脑壳</h1>
                <h2>我是身体</h2>
            </div>
        )
    }
}

// 为了展现效果定义子组件二 ChildrenTwo 是 Children的子组件 可是却经过context拿到了App组件拿过来的值 (越级传递)

class ChildrenTwo extends Component{
    constructor(){
        super()
        this.state={
        
        }
    }
    render(){
        return(
            <div>
                <button>变字</button>
                <button>变色</button>
            </div>
        )
    }
}

建立基本store

如今咱们作好了示例文件的基础模板了,而后咱们须要建立一个store.jsredux

// store.js

// 这是咱们的 reducer
const changeDom = (state,action) => {
    if(!state)return{
        text : "我是实例文字",
        color : 'red'
    }
    switch(action.type){
        case "CHANGE_TEXT":
        return{
            ...state,
            text:action.text
        }
        case "CHANGE_COLOR":
        return{
            ...state,
            color:action.color
        }
        default:
        return state
    }
}


const creatStore = (reducer)=>{
    let state = null
    const listeners = []
    const subscribe = (liestner)=>listeners.push(liestner)
    const getState = ()=>state
    const dispatch=(action)=>{
        state = reducer(state,action)
        listeners.map(item=>item())
    }
    dispatch({})
    return { getState, dispatch, subscribe }
}
export {creatStore,changeDom}

结合context使用store

咱们如今把咱们的子组件经过context公用App的store,而且把渲染方法和dispatch应用进去微信

// App.js
    import React,{Component} from 'react'
    import PropTypes from 'prop-types'  //引入
    
    // 引入 store
    import {changeDom,creatStore} from './store'

    const store = creatStore(changeDom)

    export default class App extends Component {
        // 咱们在这里把store挂到context上
        static childContextTypes = {
            store: PropTypes.object
        }
        getChildContext(){
            return{store}
        }
        constructor(){
            super()
            this.state={
              
            }
        }
        componentWillMount(){
            // console.log(hashHistory)
        }
        render() {
            return (
                <div>
                    <Children />
                    <ChildrenTwo />
                </div>
            )
        }
      
    }
    // 这里咱们再去修改咱们的子孙组建 让他们能够拿到 store
    
    // 为了展现效果定义子组件一
    
    class Children extends Component{
        static contextTypes = {
            store: PropTypes.object
          }
        constructor(){
            super()
            this.state={
                color:"",
                text:""
            }
        }

        // 这里咱们定义两个渲染方法
        getColor(){
            let store = this.context.store.getState()
            this.setState({color:store.color})
        }

        // 这里咱们定义两个渲染方法

        getText(){
            let store = this.context.store.getState()
            this.setState({text:store.text})
        }

        // 这里组件加载以前渲染
        componentWillMount(){
            this.getColor()
            this.getText()
        }
        render(){
            return(
                <div>
                    <h1 style={{color:this.state.color}}>{this.state.text}</h1>
                </div>
            )
        }
    }
    
    // 为了展现效果定义子组件二 ChildrenTwo 是 Children的子组件 可是却经过context拿到了App组件拿过来的值 (越级传递)
    
    class ChildrenTwo extends Component{
        static contextTypes = {
            store: PropTypes.object
          }
        constructor(){
            super()
            this.state={
            
            }
        }
        // 这里咱们定义 两个 action
        changeColor=()=>{
            this.context.store.dispatch({type:"CHANGE_COLOR",color:"green"})
            console.log(this.context.store.getState())
        }
        changeText=()=>{
            this.context.store.dispatch({type:"CHANGE_TEXT",text:"我变了"})
            console.log(this.context.store.getState())  //这里方便你们看到数据变了
        }
        render(){
            return(
                <div>
                    <button onClick={()=>{this.changeText()}}>变字</button>
                    <button onClick={()=>{this.changeColor()}}>变色</button>
                </div>
            )
        }
    }

显然 如今咱们点击按钮的时候,并无发生任何事情,但是咱们看console能够看到,store的数据确实变化了,这是为何呢?很简单 咱们没有把dom监听放进来,下一步咱们要设置监听。架构

设置dom监听

// Children组件 咱们在componentWillMount 中添加监听
    class Children extends Component{
        static contextTypes = {
            store: PropTypes.object
          }
        constructor(){
            super()
            this.state={
                color:"",
                text:""
            }
        }

        // 这里咱们定义两个渲染方法
        getColor(){
            let store = this.context.store.getState()
            this.setState({color:store.color})
        }

        // 这里咱们定义两个渲染方法

        getText(){
            let store = this.context.store.getState()
            this.setState({text:store.text})
        }

        // 这里组件加载以前渲染
        componentWillMount(){
            this.getColor()
            this.getText()
            this.context.store.subscribe(()=>{this.getColor()})
            this.context.store.subscribe(()=>{this.getText()})// 使用箭头函数 保证this指向

        }
        render(){
            return(
                <div>
                    <h1 style={{color:this.state.color}}>{this.state.text}</h1>
                </div>
            )
        }
    }

到这里咱们已经简单地实现了react-redux,但是你们有没有以为怪怪的?app

开始建立connect

没错 到这里咱们频繁使用context,而且每一个组件都要去手动挂context 这是至关麻烦的,如今咱们想要让这些东西都自动包裹 自动生成,咱们再怎么作呢。
咱们建立一个高阶组件就能够搞定这个问题了(一个函数,接收一个组件,生成另一个包装好的新组件)dom

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export const connect = (WrappedComponent)=>{
    class Connect extends Component{
        static contextTypes = {
            store: PropTypes.object
        }
        render(){
            return<WrappedComponent />
        }
    }
    return Connect
}

咱们这里经过 connect函数来接收一个组件 通过一层封装 返回一个包装了context的组件 可是如今这样的话 咱们每次使用都还须要大量的书写 this.context 是否是至关恶心呢? 而且每一个组件都拿到了store 有不少事它并不须要的,这时候咱们就须要告诉这个高阶组件,我这里就只须要这些东西。这就是 mapStateToProps

import React, { Component } from 'react'
import PropTypes from 'prop-types'

// 示例 这里不用
// const mapStateToProps=(state)=>{
//     return{
//         color:state.color,
//         text:state.text,
//     }
// }  就是咱们须要从store拿出来的东西

const connect= (mapStateToProps) => (WrappedComponent)=>{
    class Connect extends Component{
        static contextTypes = {
            store: PropTypes.object
        }
        render(){
            const store = this.context.stote
            let stateProps = mapStateToProps(store.getState())
            而后咱们把stateProps用...展开
            return<WrappedComponent {...stateProps}/>
        }
    }
    return Connect
}

如今咱们利用Connect改造咱们的组件

// App.js
    import React,{Component} from 'react'
    import PropTypes from 'prop-types'  //引入
    
    // 引入 store
    import {changeDom,creatStore} from './store'

    const store = creatStore(changeDom)

// ________________________________把connect_____________________________写在app.js 为的是不引入了 实际上它是单独拆分在react-redux中的
    const connect = (mapStateToProps) => (WrappedComponent)=>{
        class Connect extends Component{
            static contextTypes = {
                store: PropTypes.object
            }
            render(){
                const store = this.context.store
                console.log(store)
                let stateProps = mapStateToProps(store.getState())
                // 而后咱们把stateProps用...展开
                return<WrappedComponent {...stateProps}/>
            }
        }
        return Connect
    }
// ________________________________把connect_____________________________写在app.js 为的是不引入了 实际上它是单独拆分在react-redux中的

// app.js 的其余内容...

// ________________________________对组件一进行修改_____________________________
    
    class Children extends Component{
        constructor(){
            super()
            this.state={
                color:"",
                text:""
            }
        }

        render(){
            return(
                <div>
                    <h1 style={{color:this.props.color}}>{this.props.text}</h1>
                </div>
            )
        }
    }

    const mapStateToProps = (state) => {
        return {
            color: state.color,
            text:state.text
        }
    }
    Children = connect(mapStateToProps)(Children)
// ________________________________对组件一进行修改_____________________________

如今咱们成功把store里面的全部东西都挂到了props上面,咱们不须要去依赖context,只须要调用props便可。剩下的就是咱们的数据变动,渲染还有监听了!

对 connect 进一步改造 其实就是像咱们上面的组件同样 挂载监听

const connect = (mapStateToProps) => (WrappedComponent)=>{
        class Connect extends Component{
            static contextTypes = {
                store: PropTypes.object
            }
            componentWillMount(){
                const{store} = this.context
                this.updata()
                store.subscribe(()=>this.updata())
            }
            updata=()=>{
                 const { store } = this.context
                 let stateProps = mapStateToProps(store.getState(), this.props)
                  this.setState({
                        allProps: { // 整合普通的 props 和从 state 生成的 props
                        ...stateProps,
                        ...this.props
                        }
                    })
            }
            render(){
                // 而后咱们把allProps用...展开
                return<WrappedComponent {...this.state.allProps}/>
            }
        }
        return Connect
    }

mapDispatchToProps

state咱们改造完了,dispatch 是否是也要一块儿改造呢?答案是确定的,mapDispatchToProps就是作这个的。
咱们看看 mapDispatchToProps 是怎么写的 咱们倒着推

//const mapDispatchToProps = (dispatch) => {  // 传入的是 dispatch
//  return {  //返回一个函数
//    changeColor: (color) => {
//      dispatch({ type: 'CHANGE_COLOR', color: color })
//    }
//  }
//}


const connect = (mapStateToProps,mapDispatchToProps) => (WrappedComponent)=>{
    class Connect extends Component{
        static contextTypes = {
            store: PropTypes.object
        }
        componentWillMount(){
            const{store} = this.context
            this.updata()
            console.log(store)
            store.subscribe(()=>this.updata())
        }
        updata=()=>{
             const { store } = this.context
            let stateProps = mapStateToProps?mapStateToProps(store.getState(), this.props):{}
             let dispatchProps = mapDispatchToProps?mapDispatchToProps(store.dispatch,this.props):{}
             // 咱们要考虑到空值处理
              this.setState({
                    allProps: { // 整合普通的 props 和从 state 生成的 props
                    ...stateProps,
                    ...this.props
                    ...dispatchProps,
                    }
                })
        }
        render(){
            // 而后咱们把allProps用...展开
            return<WrappedComponent {...this.state.allProps}/>
        }
    }
    return Connect
}

到这里咱们能够把ChildrenTwo的代码也改造了

class ChildrenTwo extends Component{
        constructor(){
            super()
            this.state={
            
            }
        }
        render(){
            console.log(this.props)
            return(
                <div>
                    <button onClick={()=>{this.props.changeText("我变了")}}>变字</button>
                    <button onClick={()=>{this.props.changeColor("green")}}>变色</button>
                </div>
            )
        }
    }
    const mapDispatchToProps = (dispatch)=>{
        return {  //返回一个函数
                changeColor: (color) => {
                    dispatch({ type: 'CHANGE_COLOR', color: color })
                },
                changeText:(text)=>{
                    dispatch({ type: 'CHANGE_TEXT', text: text })
                }
            }
    }
    ChildrenTwo = connect(null,mapDispatchToProps)(ChildrenTwo)

到如今 一个简单的 react-redux已经差很少了,可是react-redux里面还有一个<Provider> 可是咱们尚未这东西 这是什么呢?实际上它作的事跟咱们App这个组件作的事一毛同样,主要就是把store挂到context上。

export class Provider extends Component{
        static childContextTypes = {
            store: PropTypes.object
        }
        getChildContext(){
            return{store}
        }
        render() {
            return (
                <div>{this.props.children}</div>
            )
        }
    }

因而 咱们如今的代码变成了这样

好了 道理讲完了 咱们如今开始拆分代码啦。

// store.js
export const creatStore = (reducer)=>{
    let state = null
    const listeners = []
    const subscribe = (liestner)=>listeners.push(liestner)
    const getState = ()=>state
    const dispatch=(action)=>{
        state = reducer(state,action)
        listeners.map(item=>item())
    }
    dispatch({})
    return { getState, dispatch, subscribe }
}
// reducer.js

// 这是咱们的 reducer  能够单独拆分红一个js文件 本身拆吧
export const changeDom = (state,action) => {
    if(!state)return{
        text : "我是实例文字",
        color : 'red'
    }
    switch(action.type){
        case "CHANGE_TEXT":
        return{
            ...state,
            text:action.text
        }
        case "CHANGE_COLOR":
        return{
            ...state,
            color:action.color
        }
        default:
        return state
    }
}
// react-redux.js
import React,{Component} from 'react'
import PropTypes from 'prop-types'  //引入

export const connect = (mapStateToProps,mapDispatchToProps) => (WrappedComponent)=>{
    class Connect extends Component{
        static contextTypes = {
            store: PropTypes.object
        }
        componentWillMount(){
            const{store} = this.context
            this.updata()
            store.subscribe(()=>this.updata())
        }
        updata=()=>{
             const { store } = this.context
             let stateProps = mapStateToProps?mapStateToProps(store.getState(), this.props):{}
             let dispatchProps = mapDispatchToProps?mapDispatchToProps(store.dispatch,this.props):{}

             // 作一下空值处理
              this.setState({
                    allProps: { // 整合普通的 props 和从 state 生成的 props
                    ...stateProps,
                    ...this.props,
                    ...dispatchProps,
                    }
                })
        }
        render(){
            // 而后咱们把allProps用...展开
            return<WrappedComponent {...this.state.allProps}/>
        }
    }
    return Connect
}


export class Provider extends Component{
        static childContextTypes = {
            store: PropTypes.object
        }
        getChildContext(){
            return {store:this.props.store}
        }
        render() {
            return (
                <div>{this.props.children}</div>
            )
        }
}
// App.js
import React,{Component} from 'react'
import Children from './Children'
import ChildrenTwo from './ChildrenTwo'
export default class App extends Component {
    constructor(){
        super()
        this.state={
          
        }
    }

    render() {
        return (
            <div>
                <Children />
                <ChildrenTwo />
            </div>
        )
    }
}
// Children.js
import React,{Component} from 'react'
import{connect} from './react-redux.js'
class Children extends Component{
    constructor(){
        super()
        this.state={
            color:"",
            text:""
        }
    }

    render(){
        return(
            <div>
                <h1 style={{color:this.props.color}}>{this.props.text}</h1>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        color: state.color,
        text:state.text
    }
}
Children = connect(mapStateToProps)(Children)

export default Children
// ChildrenTwo.js

import React,{Component} from 'react'
import{connect} from './react-redux.js'
class ChildrenTwo extends Component{
        constructor(){
            super()
            this.state={
            
            }
        }
        render(){
            return(
                <div>
                    <button onClick={()=>{this.props.changeText("我变了")}}>变字</button>
                    <button onClick={()=>{this.props.changeColor("green")}}>变色</button>
                </div>
            )
        }
    }
    const mapDispatchToProps = (dispatch)=>{
        return {  //返回一个函数
                changeColor: (color) => {
                    dispatch({ type: 'CHANGE_COLOR', color: color })
                },
                changeText:(text)=>{
                    dispatch({ type: 'CHANGE_TEXT', text: text })
                }
            }
    }
    ChildrenTwo = connect(null,mapDispatchToProps)(ChildrenTwo)
    
    export default ChildrenTwo

拆完了的代码是否是简单明了
真正工做里面 咱们须要多作两步

npm i redux --save
npm i react-redux --save

而后 咱们
对照着修改这几个位置

// creatStore 在 redux 插件中
// connect,Provider 在 react-redux 插件中
// 也就是用到哪里了 对应修改哪里 改完了你就发现了新大陆了
import { createStore } from 'redux'
import { connect,Provider } from 'react-redux'

不知不觉发现本身不只仅会用了react-redux 而且还本身实现了一个react-redux 很舒坦的呢

总结

在咱们四档下篇到这里结束了,这就是react-redux的实现和写法,你们本身去实现真正的写法,这里不作演示至关于给你们留个做业,有错误或者是有建议 或者有不懂的地方 扫码加我微信给你们解答。

视频制做中

相关文章
相关标签/搜索