在React项目中使用Redux实现计数器功能

Redux 和 React 之间没有关系。Redux 支持 ReactAngularEmber、jQuery 甚至纯 JavaScript。尽管如此,Redux 还是和 React 和 Deku 这类库搭配起来用最好,因为这类库允许你以 state 函数的形式来描述界面,Redux 通过 action 的形式来发起 state 变化。

下面我们来看一个使用 react-redux 完成计数器的案例:

最终实现的效果:点击左边的加号按钮,中间的数字加2(可以自定义参数);点击右边的减号按钮,中间的数字减1;
在React项目中使用Redux实现计数器功能
目录结构

├── index.jsx	//入口文件
├── Count.jsx	//显示页面内容的组件
├── Button.jsx	//按钮组件
├── store.js	//创建一个store,用来集中管理state dispatch action
├── reducer.js	//业务逻辑层,对数据进行操作,并返回新的state
├── connect.js	//将redux和展示组件做关联,将redux中的属性和方法挂载到组件里

安装依赖:在终端中执行下面的命令,安装 redux 和 react-redux
yarn add redux react-redux -S

store.js文件

  • 用来创建一个 store,集中管理state dispatch action,参数是 reducer。
/* store.js */
import {createStore} from 'redux'
import reducer from './reducer'

//创建store
const store=createStore(reducer)

export default store;

reducer.js文件

  • reducer 是一个纯函数,它接收两个参数 state 和 action;
  • 当处理数据时,先把原来的数据拿过来,如果没有数据,会读取初始值
  • reducer 是业务逻辑层,代码根据业务需求来写,它的作用是根据 action 传过来的内容,对数据做操作
  • reducer 最终返回一个新的state,当 state 被更改后,store.subscribe 方法里的回调函数就会执行,会通知 view 去重新获取 state,做视图的更新;
/* reducer.js */
const defaultState={count:1}
const reducer=(state=defaultState,action)=>{
    switch (action.type){
        case "inc":
            return{ count:state.count+action.num }
        case "dec":
            return{ count:state.count-1 }
        default:
            return state;
    }
}
export default reducer;

connect.js文件

  • 帮我们把redux展示组件关联,将 store 里的属性和方法挂载到组件里;
  • connect 就是一个高阶函数,把组件作为参数传进去,组件中就能读到store中的数据;
  • connect 的语法格式为: connect(mapStateToProps?, mapDispatchToProps?)(component)
    • 将第一个参数中返回的属性转化为展示组件props上的属性;
    • 将第二个参数中返回的方法转化为展示组件props上的方法;
    • 如果只想获取到方法,在第一个参数传null;
/* connect.js */
import {connect} from 'react-redux'

const mapState=(state)=>{
    return{ count:state.count }
}

const mapDispatch=(dispatch)=>{
    return{
        increm:(data)=>{ dispatch({type:"inc",num:2}) },
        decrem:()=>{ dispatch({type:"dec"}) }
    }
}

export default connect(mapState,mapDispatch)

Count.jsx

  • 使用 connect 对当前组件做一个增强,Count 组件里就可以通过 this.props 读取到 store 中的数据;
/* Count.jsx */
import React, { Component } from 'react'
import Button from './Button'
import connect from './connect'

class Count extends Component {
    render() {
        return (
            <div>
                <Button type="inc">+</Button>
                <span>{this.props.count}</span>
                <Button type="dec">-</Button>
            </div>
        )
    }
}

export default connect(Count)

Button.jsx

  • 使用 connect 对当前组件做一个增强,Button 组件里通过 this.props 调用 connect 中的方法;
  • connect 中调用 store.dispatch 方法将 action 发送到 reducer 中;
  • reducer 接收到 action,对数据进行处理后,返回新的 state;
  • 当 state 被更新的时候,store.subscribe 方法会执行,通知 view 去重新获取 state;
  • 获取到新的 state 后,会触发 render() 重新渲染页面;
/* Button.jsx */
import React, { Component } from 'react'
import connect from './connect'

class Button extends Component {
    clickHandle=()=>{
        let {type,increm,decrem} =this.props;
        if(type==="inc") increm(2)
        else decrem()
    }
    render() {
        return (
            <button onClick={this.clickHandle}>
                {this.props.children}
            </button>
        )
    }
}

export default connect(Button)

index.js

  • 在父组件最外面包裹<Provider>,并提供 store 属性,则在后代组件中都可以通过 connect 对组件做增强,读到 store 里的属性和方法;
/* index.js */
import React from 'react'
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import store from './store'
import Count from './Count'

ReactDOM.render(
  <Provider store={store}>
    <Count/>
  </Provider>,
  document.getElementById('root')
);

总结:

react-redux 中两个核心的api:

  • Provider: 提供store,根据单一store原则 ,一般只会出现在整个应用程序的最顶层。
  • connect: 用于连接展示组件和 store。

react-redux 开发思想:

Redux 的 React 绑定库是基于容器组件和展示组件相分离的开发思想。

展示组件 容器组件
作用 描述如何展现(骨架、样式) 描述如何运行(数据获取、状态更新)
直接使用 Redux
数据来源 props 监听 Redux state
数据修改 从 props 调用回调函数 向 Redux 派发 actions
调用方式 手动 通常由 React Redux 生成

大部分的组件都应该是展示型的,但一般需要少数的几个容器组件把它们和 Redux store 连接起来。技术上讲你可以直接使用 store.subscribe() 来编写容器组件。但不建议这么做的原因是无法使用 React Redux 带来的性能优化。也因此,不要手写容器组件,而使用 React Redux 的 connect() 方法来生成。

猜你喜欢

转载自blog.csdn.net/Charissa2017/article/details/105849224