资讯专栏INFORMATION COLUMN

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

FullStackDeveloper / 1950人阅读

摘要:手挽手带你学入门四档用人话教你,理解架构,以及运用在中。学完这一章,你就可以开始自己的项目了。结合搭建基础环境我们上一章讲过了的原理,内部是有一个的,只有才可以控制它变化。

手挽手带你学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 (
            
) } } // 为了展示效果定义子组件一 class Children extends Component{ constructor(){ super() this.state={ } } render(){ return(

我是脑袋

我是身体

) } } // 为了展示效果定义子组件二 ChildrenTwo 是 Children的子组件 但是却通过context拿到了App组件拿过来的值 (越级传递) class ChildrenTwo extends Component{ constructor(){ super() this.state={ } } render(){ return(
) } }
创建基本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 (
                
) } } // 这里我们再去修改我们的子孙组建 让他们可以拿到 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(

{this.state.text}

) } } // 为了展示效果定义子组件二 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(
) } }

显然 现在我们点击按钮的时候,并没有发生任何事情,可是我们看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(
                

{this.state.text}

) } }

到这里我们已经简单地实现了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
        }
    }
    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
        }
    }
    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
            }
        }
        return Connect
    }
// ________________________________把connect_____________________________写在app.js 为的是不引入了 实际上它是多带带拆分在react-redux中的

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

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

        render(){
            return(
                

{this.props.text}

) } } 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
            }
        }
        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
        }
    }
    return Connect
}

到这里我们可以把ChildrenTwo的代码也改造了

    
    class ChildrenTwo extends Component{
        constructor(){
            super()
            this.state={
            
            }
        }
        render(){
            console.log(this.props)
            return(
                
) } } 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里面还有一个 但是我们还没有这东西 这是什么呢?实际上它做的事跟我们App这个组件做的事一毛一样,主要就是把store挂到context上。

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

于是 我们现在的代码变成了这样

好了 道理讲完了 我们现在开始拆分代码啦。

// 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
        }
    }
    return Connect
}


export class Provider extends Component{
        static childContextTypes = {
            store: PropTypes.object
        }
        getChildContext(){
            return {store:this.props.store}
        }
        render() {
            return (
                
{this.props.children}
) } }
// 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 (
            
) } }
// 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(
            

{this.props.text}

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

视频制作中

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101641.html

相关文章

  • 手挽手带你学React四档(上)步一学会react-redux (自己写个Redux)

    手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中。学完这一章,你就可以开始自己的react项目了。 之前在思否看到过某个大神的redux搭建 忘记了大神的名字 这里只记得内容了 凭借记忆和当时的学习路线写下本文 隔空感谢 本人学习react-redux的时候遇到了很多坎,特别是不理解为什么这么用,这是什么东西,用来做什么。加上各种名词让人...

    sixgo 评论0 收藏0
  • 手挽手带你学React:一档 React环境搭建,语法规则,基础使用

    摘要:当属性是一个回调函数时,函数接收底层元素或类实例取决于元素的类型作为参数。 手挽手带你学React入门第一期,带你熟悉React的语法规则,消除对JSX的恐惧感,由于现在开发中都是使用ES6语法开发React,所以这次也使用ES6的模式进行教学,如果大家对ES6不熟悉的话,先去看看class相关内容吧,这里我也慢慢带大家一步一步学会React。 视频教程 视频教程可移步我的个人博客:h...

    BicycleWarrior 评论0 收藏0
  • 手挽手带你学VUE:一档 VUE简介以及常用内部指令

    摘要:这样,我们用写的就写好了。真的假的大家可以看到,这些在插值表达式内的表达式直接返回了运行完成的结果,值得一提的是,差值表达式内的规则和标签内的规则是类似的。 视频教程 由于思否不能插入视频,视频请大家移步,http://www.henrongyi.top 什么是VUE VUE是一套用于构建用户界面的渐进式框架,VUE并不是一个真正意义上的mvvm框架,它更倾向是一种数据驱动框架.所以我...

    不知名网友 评论0 收藏0
  • 手挽手带你学VUE:一档 VUE简介以及常用内部指令

    摘要:这样,我们用写的就写好了。真的假的大家可以看到,这些在插值表达式内的表达式直接返回了运行完成的结果,值得一提的是,差值表达式内的规则和标签内的规则是类似的。 视频教程 由于思否不能插入视频,视频请大家移步,http://www.henrongyi.top 什么是VUE VUE是一套用于构建用户界面的渐进式框架,VUE并不是一个真正意义上的mvvm框架,它更倾向是一种数据驱动框架.所以我...

    go4it 评论0 收藏0
  • 手挽手带你学VUE:四档 Vue-cli3 Vuex Vue-router

    摘要:安装过后到命令行执行检查版本,如果弹出版本的话恭喜你安装成功,我们开始进行下面的步骤了。全局安装的包名称由改成了。如果你已经全局安装了旧版本的或,你需要先通过卸载它。中的非常类似于事件每个都有一个字符串的事件类型和一个回调函数。 视频教程 由于思否不支持视频外链,视频请移步http://www.henrongyi.top 你能学到什么 在这一期的学习进度中,我们会开始学习在我们工作开...

    Sourcelink 评论0 收藏0

发表评论

0条评论

FullStackDeveloper

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<