手挽手帶你學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的實現和寫法,你們本身去實現真正的寫法,這裏不作演示至關於給你們留個做業,有錯誤或者是有建議 或者有不懂的地方 掃碼加我微信給你們解答。

視頻製做中

相關文章
相關標籤/搜索