详解如何优雅地在React项目中使用Redux


Posted in Javascript onDecember 28, 2017

前言

或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux

概念

首先我们会用到哪些框架和工具呢?

React

UI框架

Redux

状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux

react-redux

React插件,作用:方便在React项目中使用Redux

react-thunk

中间件,作用:支持异步action

目录结构

Tips:与Redux无关的目录已省略

|--src
 |-- store     Redux目录
  |-- actions.js
  |-- index.js
  |-- reducers.js
  |-- state.js
 |-- components  组件目录
  |-- Test.jsx
 |-- App.js    项目入口

准备工作

第1步:提供默认值,既然用Redux来管理数据,那么数据就一定要有默认值,所以我们将state的默认值统一放置在state.js文件

// state.js
// 声明默认值
// 这里我们列举两个示例
// 同步数据:pageTitle
// 异步数据:infoList(将来用异步接口获取)
export default {
 pageTitle: '首页',
 infoList: []
}

第2步:创建reducer,它就是将来真正要用到的数据,我们将其统一放置在reducers.js文件

// reducers.js
// 工具函数,用于组织多个reducer,并返回reducer集合
import { combineReducers } from 'redux'
// 默认值
import defaultState from './state.js'

// 一个reducer就是一个函数
function pageTitle (state = defaultState.pageTitle, action) {
 // 不同的action有不同的处理逻辑
 switch (action.type) {
 case 'SET_PAGE_TITLE':
  return action.data
 default:
  return state
 }
}

function infoList (state = defaultState.infoList, action) {
 switch (action.type) {
 case 'SET_INFO_LIST':
  return action.data
 default:
  return state
 }
}

// 导出所有reducer
export default combineReducers({
 pageTitle,
 infoList
})

第3步:创建action,现在我们已经创建了reducer,但是还没有对应的action来操作它们,所以接下来就来编写action

// actions.js
// action也是函数
export function setPageTitle (data) {
 return (dispatch, getState) => {
 dispatch({ type: 'SET_PAGE_TITLE', data: data })
 }
}

export function setInfoList (data) {
 return (dispatch, getState) => {
 // 使用fetch实现异步请求
 window.fetch('/api/getInfoList', {
  method: 'GET',
  headers: {
   'Content-Type': 'application/json'
  }
 }).then(res => {
  return res.json()
 }).then(data => {
  let { code, data } = data
  if (code === 0) {
   dispatch({ type: 'SET_INFO_LIST', data: data })
  }
 })
 }
}

最后一步:创建store实例

// index.js
// applyMiddleware: redux通过该函数来使用中间件
// createStore: 用于创建store实例
import { applyMiddleware, createStore } from 'redux'

// 中间件,作用:如果不使用该中间件,当我们dispatch一个action时,需要给dispatch函数传入action对象;但如果我们使用了这个中间件,那么就可以传入一个函数,这个函数接收两个参数:dispatch和getState。这个dispatch可以在将来的异步请求完成后使用,对于异步action很有用
import thunk from 'redux-thunk'

// 引入reducer
import reducers from './reducers.js'

// 创建store实例
let store = createStore(
 reducers,
 applyMiddleware(thunk)
)
export default store

至此,我们已经完成了所有使用Redux的准备工作,接下来就在React组件中使用Redux

开始使用

首先,我们来编写应用的入口文件APP.js

import React from 'react'
import ReactDOM from 'react-dom'
// 引入组件
import TestComponent from './components/Test.jsx'

// Provider是react-redux两个核心工具之一,作用:将store传递到每个项目中的组件中
// 第二个工具是connect,稍后会作介绍
import { Provider } from 'react-redux'
// 引入创建好的store实例
import store from '@/store/index.js'

// 渲染DOM
ReactDOM.render (
 (
 <div>
  {/* 将store作为prop传入,即可使应用中的所有组件使用store */}
  <Provider store = {store}>
   <TestComponent />
  </Provider>
 </div>
 ),
 document.getElementById('root')
)

最后是我们的组件:Test.jsx

// Test.jsx
import React, { Component } from 'react'

// connect方法的作用:将额外的props传递给组件,并返回新的组件,组件在该过程中不会受到影响
import { connect } from 'react-redux'

// 引入action
import { setPageTitle, setInfoList } from '../store/actions.js'
class Test extends Component {
 constructor(props) {
 super(props)
 }
 componentDidMount () {
 let { setPageTitle, setInfoList } = this.props
 
 // 触发setPageTitle action
 setPageTitle('新的标题')
 
 // 触发setInfoList action
 setInfoList()
 }

 render () {
 // 从props中解构store
 let { pageTitle, infoList } = this.props
 
 // 使用store
 return (
  <div>
  <h1>{pageTitle}</h1>
  {
   infoList.length > 0 ? (
    <ul>
     {
      infoList.map((item, index) => {
       <li>{item.data}</li>
      })
     }
    </ul>
   ):null
  }
  </div>
 )
 }
}

// mapStateToProps:将state映射到组件的props中
const mapStateToProps = (state) => {
 return {
 pageTitle: state.pageTitle,
 infoList: state.infoList
 }
}

// mapDispatchToProps:将dispatch映射到组件的props中
const mapDispatchToProps = (dispatch, ownProps) => {
 return {
 setPageTitle (data) {
  // 如果不懂这里的逻辑可查看前面对redux-thunk的介绍
  dispatch(setPageTitle(data))
  // 执行setPageTitle会返回一个函数
  // 这正是redux-thunk的所用之处:异步action
  // 上行代码相当于
  /*dispatch((dispatch, getState) => {
   dispatch({ type: 'SET_PAGE_TITLE', data: data })
  )*/
 },
 setInfoList (data) {
  dispatch(setInfoList(data))
 }
 }
}
export default connect(mapStateToProps, mapDispatchToProps)(Test)

Redux三大原则

1、单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中

2、State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象

3、使用纯函数来执行修改

为了描述 action 如何改变 state tree ,你需要编写 reducers

结语

以上就是在React项目中使用Redux的简单示例,文中代码可能会有编写错误,欢迎指正,同时希望本文对大家有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript实现仿WebQQ界面的“浮云”兼容 IE7以上版本及FF
Apr 27 Javascript
为什么要在引入的css或者js文件后面加参数的详细讲解
May 03 Javascript
jQuery使用hide方法隐藏页面上指定元素的方法
Mar 30 Javascript
onmouseover事件和onmouseout事件全面理解
Aug 15 Javascript
jQuery实现的超链接提示效果示例【附demo源码下载】
Sep 09 Javascript
基本DOM节点操作
Jan 17 Javascript
react.js 获取真实的DOM节点实例(必看)
Apr 17 Javascript
JavaScript 上传文件(psd,压缩包等),图片,视频的实现方法
Jun 19 Javascript
vscode中eslint插件的配置(prettier配置无效)
Sep 10 Javascript
浅谈layui数据表格判断问题(加入表单元素),设置单元格样式
Oct 26 Javascript
浅谈vue生命周期共有几个阶段?分别是什么?
Aug 07 Javascript
vue 导出文件,携带请求头token操作
Sep 10 Javascript
ionic3实战教程之随机布局瀑布流的实现方法
Dec 28 #Javascript
JS实现带动画的回到顶部效果
Dec 28 #Javascript
JavaScript实现元素滚动条到达一定位置循环追加内容
Dec 28 #Javascript
在nginx上部署vue项目(history模式)的方法
Dec 28 #Javascript
js实现把时间戳转换为yyyy-MM-dd hh:mm 格式(es6语法)
Dec 28 #Javascript
vue获取dom元素注意事项
Dec 28 #Javascript
vue实现文章内容过长点击阅读全文功能的实例
Dec 28 #Javascript
You might like
PHP url 加密解密函数代码
2011/08/26 PHP
PHP实现数组和对象的相互转换操作示例
2019/03/20 PHP
Javascript 匿名函数及其代码模式原理
2010/03/19 Javascript
JQuery触发事件例如click
2013/09/11 Javascript
Windows 系统下安装和部署Egret的开发环境
2014/07/31 Javascript
用Jquery选择器计算table中的某一列某一行的合计
2014/08/13 Javascript
js时间日期格式化封装函数
2014/12/02 Javascript
node.js中的socket.io的广播消息
2014/12/15 Javascript
基于jQuery实现美观且实用的倒计时实例代码
2015/12/30 Javascript
jquery插件jquery.dragscale.js实现拖拽改变元素大小的方法(附demo源码下载)
2016/02/25 Javascript
深入理解bootstrap框架之入门准备
2016/10/09 Javascript
微信小程序 地图map实例详解
2017/06/07 Javascript
JavaScript实现获取select下拉框中第一个值的方法
2018/02/06 Javascript
NodeJS 实现多语言的示例代码
2018/09/11 NodeJs
Vue实现的父组件向子组件传值功能示例
2019/01/19 Javascript
详解原生JS动态添加和删除类
2019/03/26 Javascript
vue.js 2.*项目环境搭建、运行、打包发布的详细步骤
2019/05/01 Javascript
js定义类的方法示例【ES5与ES6】
2019/07/30 Javascript
Vue 自定义标签的src属性不能使用相对路径的解决
2019/09/17 Javascript
[52:29]DOTA2上海特级锦标赛主赛事日 - 2 胜者组第一轮#3Secret VS OG第三局
2016/03/03 DOTA
[03:06]3分钟带你回顾DOTA2完美盛典&完美大师赛
2017/12/06 DOTA
python 绘制拟合曲线并加指定点标识的实现
2019/07/10 Python
python动态规划算法实例详解
2020/11/22 Python
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
html5菜单折纸效果
2014/04/22 HTML / CSS
Shell如何接收变量输入
2016/08/06 面试题
学生学习总结的自我评价
2013/10/22 职场文书
班级文化建设标语
2014/06/23 职场文书
学校交通安全责任书
2014/08/25 职场文书
运动会跳远广播稿5篇
2014/09/17 职场文书
法人身份证明书
2014/10/08 职场文书
python爬虫之爬取笔趣阁小说
2021/04/22 Python
详解Vue的sync修饰符
2021/05/15 Vue.js
一条慢SQL语句引发的改造之路
2022/03/16 MySQL
Python实现将多张图片合成MP4视频并加入背景音乐
2022/04/28 Python
CSS中calc(100%-100px)不加空格不生效
2023/05/07 HTML / CSS