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

栏目: 服务器 · 发布时间: 5年前

内容简介:手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中。学完这一章,你就可以开始自己的react项目了。上一篇我们自己实现了Redux,这一篇我们来看看如何去实现一个react-redux本文需要用到的知识

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

视频教程

上一篇我们自己实现了Redux,这一篇我们来看看如何去实现一个react-redux

开始之前

本文需要用到的知识

首先你要会React的基础(这是废话)

高阶组件

React context

满足这三项我们开始往下看。

react结合redux

搭建基础环境

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

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

// 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.js

// 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,可是大家有没有觉得怪怪的?

开始创建connect

没错 到这里我们频繁使用context,并且每个组件都要去手动挂context 这是相当麻烦的,现在我们想要让这些东西都自动包裹 自动生成,我们再怎么做呢。

我们创建一个高阶组件就可以搞定这个问题了(一个函数,接收一个组件,生成另外一个包装好的新组件)

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的实现和写法,大家自己去实现真正的写法,这里不做演示相当于给大家留个作业,有错误或者是有建议 或者有不懂的地方 扫码加我微信给大家解答。

视频制作中


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Design Accessible Web Sites

Design Accessible Web Sites

Jeremy Sydik / Pragmatic Bookshelf / 2007-11-05 / USD 34.95

It's not a one-browser web anymore. You need to reach audiences that use cell phones, PDAs, game consoles, or other "alternative" browsers, as well as users with disabilities. Legal requirements for a......一起来看看 《Design Accessible Web Sites》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换