前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >深入浅出redux知识

深入浅出redux知识

原创
作者头像
Qiang
修改于 2019-06-12 09:50:50
修改于 2019-06-12 09:50:50
9980
举报
文章被收录于专栏:前端精髓前端精髓

redux状态管理的容器。一般在react中使用。

代码语言:txt
AI代码解释
复制
npm install redux

开始使用

代码语言:txt
AI代码解释
复制
// 定义常量
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
// 编写处理器函数
const initState = { num: 0 }
function reducer(state = initState, action) {
  switch (action.type) {
    case INCREMENT:
      return { num: state.num + 1 }
    case DECREMENT:
      return { num: state.num - 1 }
    default:
      return state
  }
}
// 创建容器
const store = createStore(reducer)

reducer函数需要判断动作的类型去修改状态,需要注意的是函数必须要有返回值。此函数第一个参数是 state 状态,第二个参数是 action 动作,action 参数是个对象,对象里面有一个不为 undefinedtype 属性,就是根据这个属性去区分各种动作类型。

在组件中这样使用

代码语言:txt
AI代码解释
复制
const actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}
class Counter extends Component {
  constructor(props) {
    super(props);
    // 初始化状态
    this.state = {
      num: store.getState().num
    }
  }
  componentDidMount() {
    // 添加订阅
    this.unsubscribe = store.subscribe(() => {
      this.setState({ num: store.getState().num })
    })
  }
  componentWillUnmount() {
    // 取消订阅
    this.unsubscribe()
  }
  increment = () => {
    store.dispatch(actions.increment())
  }
  decrement = () => {
    store.dispatch(actions.decrement())
  }
  render() {
    return (
      <div>
        <span>{this.state.num}</span>
        <button onClick={this.increment}>加1</button>
        <button onClick={this.decrement}>减1</button>
      </div>
    );
  }
}

我们都知道组件中的 stateprops 改变都会导致视图更新,每当容器里面的状态改变需要修改 state,此时就需要用到 store 中的 subscribe 订阅这个修改状态的方法,该方法的返回值是取消订阅,要修改容器中的状态要用store 中的 dispatch 表示派发动作类型,store 中的 getState 表示获取容器中的状态。

bindActionCreators

为了防止自己手动调用 store.dispatch ,一般会使用redux的这个 bindActionCreators 方法来自动绑定 dispatch 方法,用法如下。

代码语言:txt
AI代码解释
复制
let actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}

actions = bindActionCreators(actions, store.dispatch)

class Counter extends Component {
  constructor(props) {
    super(props);
    // 初始化状态
    this.state = {
      num: store.getState().num
    }
  }
  componentDidMount() {
    // 添加订阅
    this.unsubscribe = store.subscribe(() => {
      this.setState({ num: store.getState().num })
    })
  }
  componentWillUnmount() {
    // 取消订阅
    this.unsubscribe()
  }
  increment = () => {
    actions.increment()
  }
  decrement = () => {
    actions.decrement()
  }
  render() {
    return (
      <div>
        <span>{this.state.num}</span>
        <button onClick={this.increment}>加1</button>
        <button onClick={this.decrement}>减1</button>
      </div>
    );
  }
}

export default Counter;

react-redux

代码语言:txt
AI代码解释
复制
npm install react-redux

这个库是连接库,用来和react和redux进行关联的,上面使用redux的时候发现一个痛点就是要订阅设置状态的方法还要取消订阅,而react-redux却可以通过props自动完成这个功能。

代码语言:txt
AI代码解释
复制
import {Provider} from 'react-redux'
import {createStore} from 'redux'

const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'

const initState = { num: 0 }
function reducer(state = initState, action) {
  switch (action.type) {
    case INCREMENT:
      return { num: state.num + 1 }
    case DECREMENT:
      return { num: state.num - 1 }
    default:
      return state
  }
}
const store = createStore(reducer)

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

Provider 是个高阶组件,需要传入store参数作为store属性,高阶组件包裹使用状态的组件。这样就完成了跨组件属性传递。

代码语言:txt
AI代码解释
复制
import {connect} from 'react-redux'
代码语言:txt
AI代码解释
复制
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
let actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}class Counter extends Component {
  render() {
    return (  <div>
    <span>{this.props.num}</span>
    <button onClick={() => this.props.increment()}>加1</button>
    <button onClick={() => this.props.decrement()}>减1</button>  </div>
);  }
}
const mapStateToProps = state => {
  return state
}
const mapDispatchToProps = actions
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

组件中使用connect方法关联组件和容器,这个高阶函数,需要执行两次,第一次需要传入两个参数,第一个参数是将状态映射为属性,第二个是将action映射为属性,第二次需要传入组件作为参数。

mapStateToProps

该参数是个函数返回对象的形式,参数是store中的 state,可以用来筛选我们需要的属性,防止组件属性太多,难以维护

比如我们状态是这样的{ a: 1, b: 2 } 想改成这样的{ a: 1 },使用如下

代码语言:txt
AI代码解释
复制
const mapStateToProps = state => {
  return { a: state.a }
}

mapDispatchToProps

这个方法将action中的方法映射为属性,参数是个函数返回对象的形式,参数是store中的 dispatch,可以用来筛选action

代码语言:txt
AI代码解释
复制
let actions = {
  increment() {
    return { type: INCREMENT }
  },
  decrement() {
    return { type: DECREMENT }
  }
}

现在action中有两个方法,我们只需要一个的话就可以这么做了。

代码语言:txt
AI代码解释
复制
const mapDispatchToProps = dispatch => {
  return {
    increment: (...args) => dispatch(actions.increment(...args))
  }
}

redux原理


createStore原理

现在你已经掌握了react和react-redux两个库的使用,并且知道他们的作用分别是干什么的,那么我们就看看原理,先学习redux原理,先写一个createStore方法。

代码语言:txt
AI代码解释
复制
import createStore from './createStore'


export {
  createStore
}

回顾一下createStore是怎么使用的,使用的时候需要传入一个处理器reducer函数,根据动作类型修改状态然后返回状态,只有在调用dispatch方法修改状态的时候才会执行reducer 才能得到新状态。

代码语言:txt
AI代码解释
复制
import isPlainObject from './utils/isPlainObject'
import ActionTypes from './utils/actionTypes'

function createStore(reducer, preloadedState) {
  let currentState = preloadedState
  function getState() {
    return currentState
  }
  function dispatch(action) {
    // 判断是否是纯对象
    if (!isPlainObject(action)) {
      throw new Error('类型错误')
    }
    // 计算新状态
    currentState = currentReducer(currentState, action)
  }
  dispatch({ type: ActionTypes.INIT })
  return {
    dispatch,
    getState
  }
}

export default createStore

在调用 dispatch 方法的时候,需要传入一个对象,并且有个 type 属性,为了保证传入的参数的正确性,调用了isPlainObject 方法,判断是否是一个对象。

代码语言:txt
AI代码解释
复制
function isPlainObject (obj) {
  if (typeof obj !== 'object' || obj === null) {
    return false
  }
  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  return Object.getPrototypeOf(obj) === proto
}

export default isPlainObject

该方法的原理就是判断这个对象的原型和 Object.prototype 是否相等。

redux中还有订阅和取消订阅的方法,每当状态改变执行订阅的函数。发布订阅是我们再熟悉不过的原理了,我就不多说了。

代码语言:txt
AI代码解释
复制
function createStore(reducer, preloadedState) {
  let currentState = preloadedState
  let currentReducer = reduce
  let currentListeners = []
  let nextListeners = currentListeners
  // 拷贝
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }
  
  function getState() {
    return currentState
  }
  
  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('类型错误')
    }
    // 订阅
    ensureCanMutateNextListeners()
    nextListeners.push(listener)
    return function unsubscribe() {
      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    // 判断是否是纯对象
    if (!isPlainObject(action)) {
      throw new Error('类型错误')
    }

    // 计算新状态
    currentState = currentReducer(currentState, action)
    // 发布
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
  }

  dispatch({ type: ActionTypes.INIT })
  return {
    dispatch,
    getState,
    subscribe
  }
}

ensureCanMutateNextListeners 的作用是,如果是在 listeners 被调用期间发生订阅(subscribe)或者解除订阅(unsubscribe),在本次通知中并不会立即生效,而是在下次中生效。

代码里面有个值得注意的是调用了一次dispatch 方法,派发一次动作,目的是为了得到默认值,而且为了这个动作类型与众不同,防止定义的类型冲突,所以redux这么来写。

代码语言:txt
AI代码解释
复制
const randomString = () => Math.random().toString(36).substring(7).split('').join('.')

const ActionTypes = {
  INIT: `@@redux/INIT${randomString()}`
}

export default ActionTypes

bindActionCreators原理

bindActionCreators 在上面已经介绍了他的作用,就是为每个方法自动绑定dispatch方法。

代码语言:txt
AI代码解释
复制
export default function bindActionCreators(actionCreators, dispatch) {
  function bindActionCreator(actionCreators, dispatch) {
    return function () {
      return dispatch(actionCreators.apply(this, arguments))
    }
  }

  if (typeof actionCreators === 'function') {
    bindActionCreator(actionCreators, dispatch)
  }

  const boundActionCreator = {}
  for (const key in actionCreators) {
    boundActionCreator[key] = bindActionCreator(actionCreators[key], dispatch)
  }
  return boundActionCreato

}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
PowerBI: 使用计算组功能计算不同度量值的同比、环比
文章背景: 在进行商业数据分析时,经常需要给不同的度量值(如销售额、销量等)计算同比、环比、YTD(年初至今)等指标,如果给每个指标都写一个以上的时间智能函数,那么会写很多重复的度量值,这些度量值的唯一不同就在于引用的基础度量值。比如:上月业绩 = CALCULATE([销售业绩],DATEADD('日期表'[日期],-1,MONTH))。
Exploring
2022/12/18
4K0
PowerBI: 使用计算组功能计算不同度量值的同比、环比
大数据分析工具Power BI(七):DAX使用场景及常用函数
Power BI中DAX函数非常多,功能非常强大,下面结合一些实际场景来讲解DAX一些常用的函数,这些场景包含求和、计数、相除、排序、累计、环比、同比,为了更方便后续的可视化展示数据,我们新创建可视化展示的页面,创建一个新表存储后续展示的度量值,具体操作如下:
Lansonli
2023/03/27
10.3K0
大数据分析工具Power BI(七):DAX使用场景及常用函数
PowerBI DAX 计算组 基础篇
随着 PowerBI 在2020.7月的发布,迎来一个重要的功能:计算组(Caculation Group)。
BI佐罗
2020/07/21
4K0
PowerBI DAX 计算组 基础篇
PowerBI & Excel CEO 终极驾驶舱 - 第二弹 - 综合近期与历史分析
长期关注PowerBI战友联盟的战友会发现,我们现在的很多文章出现了连载的迹象。我们在此前的文章以及系统化的视频教程中已经讲解了PowerBI及DAX基础部分,我们的文章将不断基于这些基础给出非常现实的设计。每篇文章可能会以及此前的文章,并重点解决某类痛点,最后给出一个综合的标准实现。
BI佐罗
2019/09/23
1.7K0
PowerBI & Excel CEO 终极驾驶舱 - 第二弹 - 综合近期与历史分析
PowerBI 时间智能终极奥义,用 WTD 练手
单纯讲解时间智能函数犹如盲人摸象,不见全貌,更不见本质。 我们之前写过很多关于时间智能函数的文章,但文本将是最为本质以及最重要的。本文属于 BI佐罗 PowerBI VIP 线下培训部分抽取。
BI佐罗
2020/06/04
1.4K0
一步一步教你制作销售目标分析报告
  前面的文章中我已经使用了一个入门案例动态销售报告来带领大家入门PowerBI的入门学习,基于动态销售报告,我可以在来进行细化处理销售目标表中的数据。本文的主题就是销售目标的分析。我们都知道销售目标是销售的起点,销售人员每天的跟进都可以来反映销售目标完成情况。因此,将销售目标的颗粒度细化到每一天很有必要。   销售目标的细化主要的难点在于许多的企业在销售业务中有季节性。比如说在相同的月份中,去年的2月和今年的2月可能天数不同,无法全部复制。还有就是月份中的周末时间,有些月份存在4个周末,有些月份存在5个周末。这些时间因素都会对销售趋势造成一定的影响。   回到数据源结构,我们回顾一下动态销售报告中的销售明细数据。这个表中有销售日期和销售额,我们可以使用DAX函数来将销售目标处理到该表的汇总数据表中。接下就一起来处理数据吧。在PowerQuery中手动输入销售目标表
黄昏前黎明后
2020/02/13
1.9K0
零售行业的店铺盈利了没到底怎么算,看PowerBI帮你实现
判断一家门店经营好坏,通常会选择参照物进行比较,可以是不同时间区间和自身的同环比,也可以在同一个时间区间不同部门间横向比较,或是和某个标准、标杆比较。本节重点介绍对比分析中的一个关键点,计算口径的问题。
BI佐罗
2020/11/25
1.2K0
零售行业的店铺盈利了没到底怎么算,看PowerBI帮你实现
如何巧妙的使用Power BI计算同比增长
小SUN目前就职于一家葡萄酒分销公司,其主要职责就是为业务部门提供数据分析报告,其中一份报告是追踪销售团队的KPI并与去年同期进行对比。
公众号PowerBI大师
2019/11/12
8.3K0
如何巧妙的使用Power BI计算同比增长
PowerBI系列之入门案例动态销售报告
  本文将讲解如何从零开始使用PowerBI Desktop制作一份动态销售报告。帮助大家快速入门PowerBI Desktop的操作。我们先来看一下一份动态销售报告的构成。 1、左上角放置了小黎子数据分析的二维码图片,紧接着是切片器,由城市,店长,店铺数据默认情况下是所有的数据,点击下拉框可以进行筛选数据 2、右上角是放置的卡片图,主要用于显示报告分析中重要的指标。 3、中间部分的图表显示的业绩排名,业绩贡献,业绩增长情况 4、左下角的散点图,使用了十字线将所有员工分为四个象限,右上角就是指标最佳的员工,左下角就是指标比较差的人员。圆圈大小代表着业绩金额大小。 5、右下角用表展现店铺的销售数据情况。
黄昏前黎明后
2019/10/23
5.4K1
PowerBI系列之入门案例动态销售报告
【DAX 系列】PowerBI 期初期末的数据结构与过程计算模式
通俗来讲,可以被累加数学运算的数字字段就是可度量字段,例如:销售额,利润,成本。本文字段除了计数不可以累加,叫做不可累加字段,如:地点,姓名,手机号等。
BI佐罗
2020/02/26
2.9K0
PowerBI DAX 度量值管理 - 基本编写到高级管理
我们会用几篇文章来描述这些问题如何在当前的 PowerBI 中实现。很多问题的解决并不是能用 PowerBI 内置功能解决,这也算是一个痛点,按照微软的表述,微软会比较接纳社区的第三方插件与 PowerBI 的结合,一方面可以不必重复劳动,一方面很多社区插件已经注入很多心血,值得复用。
BI佐罗
2020/06/24
2.3K0
PowerBI DAX 度量值管理 - 基本编写到高级管理
巧用数据分析表达式,让数据指标创建更简单
只需要获取当日累计的销售额,于是店老板就用 Excel或者纸质的表格创建了一个表,表中包含销售的日期时间,销售的产品,销售的数量,以及卖出的单价是多少。如此每天进行一个汇总,或者月底进行汇总就可以知道当天或当月的销售额是什么情况了。
葡萄城控件
2023/01/04
9940
巧用数据分析表达式,让数据指标创建更简单
PowerBI中同比环比那点事
哈喽,这里是白茶。一个PowerBI的初学者,记得在刚开始学DAX的时候,一个同比环比的问题困扰了我很久,每次都是觉得自己刚刚理解一点东西了,但是发现后续的坑更多。话不多说,LOOK!
PowerBI丨白茶
2021/09/02
2.7K0
PowerBI中同比环比那点事
最实用的帕累托分析模板
很多人都知道80/20帕累托法则(20%的人掌握着80%的财富),而ABC分类法可以说是该法则的衍生,目的是把握关键,分清主次。
公众号PowerBI大师
2019/08/07
1.9K0
最实用的帕累托分析模板
2.16 PowerBI数据建模-时间智能函数
加入 PowerBI自己学 知识星球 可以:下载源文件,边学边练;遇到问题,提问交流,有问必答。
PowerBI自己学_轻松
2025/02/24
630
2.16 PowerBI数据建模-时间智能函数
Power BI追踪春节业绩实操
春节不同于其他节日,许多零售企业春节的销售高峰不是节日期间,而是春节前的两周。这两周的销售对全年的业绩目标实现都会产生重要的影响。
wujunmin
2022/02/09
2.6K0
Power BI追踪春节业绩实操
用PowerBI分析上市公司财务数据(三)
在用power bi 分析上市公司财务数据(二)中我们知道利润表的数据与资产负债表数据有所不同,一般情况下,我们选择某月或某个季度,对利润表而言,往往首先是想知道在当月或当季下的值,由于我们获得到的财务报表是年累计数,因此,要想知道每个季度的值,需要用本年累计数减去本年至上个季度的累计数(一季度除外)。
公众号PowerBI大师
2019/12/03
4.1K0
用PowerBI分析上市公司财务数据(三)
销售需求丨周分析
咋说呢,白茶之前分享过关于月度环比、年同比、日环比的问题,有的小伙伴就问我说,咋不弄个周环比呢?白茶一寻思,也对!不差这一个!本期呢,白茶决定分享一下做周环比的思路。
PowerBI丨白茶
2021/09/03
7700
销售需求丨周分析
【Quick BI VS Power BI】(一)
Quick BI(以下简称Qbi)做数据分析有5个模块:仪表板、电子表格、数据大屏、即席分析和自主取数。其中仪表板和即席分析比较接近于Power BI(以下简称Pbi)制作的报告。本文的比较对象,主要指Qbi的仪表板和Pbi的报告。
btharp
2024/02/17
7040
【Quick BI VS Power BI】(一)
PowerBI 中正确计算MTD的去年同期
本文来自伙伴从实际案例的问题。在 PowerBI 中,时间智能计算是一个老生常谈的问题,但在实际中可能会出现各种变种,这就要求我们灵活处理。
BI佐罗
2019/10/24
3.5K0
推荐阅读
相关推荐
PowerBI: 使用计算组功能计算不同度量值的同比、环比
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文