redux学习笔记(一)——redux基础知识

一、前言

在使用react等前端MVVM框架的时候,比较好的开发模式是组件化开发——将具有相似功能的元素抽象成一个组件,这样就可以在不同页面中引用该组件,减少了代码量,同时也缩减了开发成本。

引入组件化开发的影响就是,在开发过程中需要面对纷繁复杂的组件之间、页面之间的通信问题,虽然react有提供props概念,允许父组件向子组件传参,但是面对更多“子组件向父组件传参”、“子组件向兄弟组件传参”等问题时就显得有些无力。

当然,只使用props也不是不能实现奇奇怪怪的传参需求,只是实现起来比较费劲且实现方式不够优雅罢了。

这也是我学习redux的重要原因——需要一种更优雅更有效的状态管理方式。

不过也需要注意一个误区,不要“唯redux是瞻”,redux虽然好。引用曾经有一个人的话说就是——如果你不知道是否需要 Redux,那就是不需要它。

用我的话来讲,就是使用redux之前需要先评估一下不使用redux会面临多少的工作量与使用redux之后需要面临多少的工作量,请记住没有哪一种技术的引入是完全缩减开发成本的而不带有别的成本的,引入之前的学习成本也属于成本。

闲话不多说,都点进来了,我想以上的问题你也都考虑的比较清楚了。

二、redux基础

2.1 安装redux

先从安装说起,使用npm安装redux

npm install redux --save

2.2 redux的基本组成

redux由一下三部分组成:

  • store
  • action
  • reducer

2.2.1 store

store从字面意思就可以知道,他是和存储相关的 (废话,redux整个都和存储有关系)

这里插入一句话,redux的作用就是在你的应用全局维护一个对象,这个对象存储着你想要存储的所有数据。既然可以存数据,那用于组件之间的通信也是洒洒水啦。

store就是一个数据仓库(就是维护了一个对象,这个对象存储了你想要存储的数据,说得这么高大上=。=)。不过请注意,一个应用有且仅有一个store(单一数据源)。且store是只读的

因为state是只读的,所以无法在应用中直接对state变量进行修改,如果想要修改state必须使用store.dispatch()方法,该方法参数是一个对象,该对象必须包含一个type属性,用于明确触发的是什么操作来进行state的修改。有点类似于事件绑定,这里触发事件,然后响应的事件监听器接收到事件后执行相应的处理。

  • 定义一个store
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;

第一行:引入createStore方法,该方法用于实例化一个store
第二行:从本地文件引入reducer,实例化store的时候,需要将该reducer作为参数。(reducer的定义请继续往下看)
第三行:以reducer为参数,调用createStore方法,实例化一个store
第四行:暴露该store以供外部引用。

2.2.2 reducer

既然用store.dispatch()方法时“触发事件”,那么“事件监听”又在哪里定义呢?这就是reducer的工作了。reducer用来接收所有的store.dispatch()方法发来的消息,并根据对应的type值,执行相应的关于store的操作。注意修改store必须使用纯函数

可能又有人要问了,什么是纯函数?已知函数的参数是一个引用类型,在函数的执行过程中,修改了该引用类型的值或者引用,这都不叫纯函数;纯函数就是在函数执行过程中不会对该引用类型的值或者引用做任何修改,对原有变量没有任何副作用的函数,就叫纯函数。

例如以下两个例子:

  • 非纯函数
const user = {
  name: 'zhangsan'
};

function notPureFunc(param) {
  param.name = 'lisi';
  return param;
}

const result = notPureFunc(user);
console.log('user = ', user);
console.log('result = ', result);

notPureFunc()方法执行完之后,原有的引用类型变量user的属性值也受到了影响,最终输出结果为:

user = { name: 'lisi' }
result = { name: 'lisi' }
  • 纯函数
const user = {
  name: 'zhangsan'
};

function pureFunc(param) {
  return Object.assign({}, param, { name: 'lisi' });
}

const result = pureFunc(user);
console.log('user = ', user);
console.log('result = ', result);

此时pureFunc()方法执行完之后,原有的引用类型变量user的值并未受到改变,仅仅只是再次基础上重新生成了一个新的对象并返回。最终输出结果为:

user =  { name: 'zhangsan' }
result =  { name: 'lisi' }

reducer中所有关于store的修改都要使用纯函数进行,并且返回一个全新的store对象(该对象就是经过修改之后的新对象)。

  • 定义一个reducer
const reducer = (state, action) => {
  switch (action.type) {
    case 'add':
      return { value: action.value };
    default:
      return state;
  }
}

export default reducer;

reducer根据actiontype属性来分别执行对应的操作,并且返回一个新的state值。

定义的reducer方法需要定义两个参数,如下表:

参数名类型说明
stateObject原本的state
actionObject调用的action对象

2.2.3 action

在使用store.dispatch()方法时,每次都要传入一个对象,作为一个数据仓库,修改同一个属性的操作可以归为一类操作,该类操作可以使用同一个type值,如果每次在调用store.dispatch()是都要单独写store.dispatch({ type: 'add', value: 1 })这样的形式,就会显得比较冗余,且没有将同一type值的action进行统一管理,在日后的维护中会比较难以维护。

这里就需要引入action的概念。

action是把数据从应用传到store的载体。一般来说,可以说成是通过store.dispatch()将action传递给store。

其实说了这么多行业黑话,什么载体……不如看一下代码:

  • 一个action的定义
const addAction = (value) => {
  return {
    type: 'add',
    value
  }
};

丫就是把store.dispatch()方法中的参数统一用一个工厂函数封装了一下罢了,每次调用的时候,实例化一个对应的对象来使用。

三、Show me the code

以下从一个示例来演示如何在react应用中使用redux。该示例将会在页面中设置两个按钮,一个【+】和一个【-】,并在旁边显示一个计数器。

3.1 创建一个react程序

创建一个redux-demo程序

npx create-react-app redux-demo

创建完之后,进入该react-demo文件夹。

3.2 安装redux

进入该项目文件之后,执行下面语句安装redux

npm i redux --save

3.3 创建reducer

redux-demo/src文件夹下创建一个文件夹reducer,并在该文件夹内创建index.js文件,编写相关reducer代码如下:

const reducer = (state, action) => {
  switch(action.type) {
    case 'add':
      return { ...state, value: state.value + 1 };
    case 'decrese':
      return { ...state, value: state.value - 1 };
    default:
      return state;
  }
}

export default reducer;

约定了两个action.type值的操作,当点击【+】按钮时,执行add条件语句;当点击【-】按钮时,执行decrese条件语句。其余的action则不会有任何修改,返回原本的state

这样写的话,程序最开始启动后,state的值将会是一个undefined,所以我们需要对state参数赋予一个初始值。修改后的代码如下:

const initState = {
  value: 0,
};

const reducer = (state = initState, action) => {
  switch(action.type) {
    case 'add':
      return { ...state, value: state.value + 1 };
    case 'decrese':
      return { ...state, value: state.value - 1 };
    default:
      return state;
  }
}

export default reducer;

3.4 创建store

redux-demo/src文件夹下创建一个文件夹store,并在该文件夹内创建index.js文件,编写相关store代码如下:

import { createStore } from 'redux';

import reducer from '../reducer';

const store = createStore(reducer);

export default store;

3.5 创建action

redux-demo/src文件夹下创建一个文件夹action,并在该文件夹内创建index.js文件,编写相关action代码如下:

export const addAction = () => {
  return {
    type: 'add',
  }
}

export const decreseAction = () => {
  return {
    type: 'decrese',
  }
}

分别定义了两个action

  • addAction
  • decreseAction

这两个action都会返回一个带有type属性的对象。

3.6 修改app.js

为该应用创建两个按钮,和一个显示当前计数的元素。

修改src/app.js文件内容如下:

import React from 'react';
import store from './store';
import { addAction, decreseAction } from './action';

class App extends React.Component {
  
  addHandler = () => {
    store.dispatch(addAction());
  }

  decreseHandler = () => {
    store.dispatch(decreseAction());
  };

  render() {
    return (
      <div className="App">
        <button onClick={this.addHandler}>+</button>
        <button onClick={this.decreseHandler}>-</button>
        <div>{store.getState().value}</div>
      </div>
    ); 
  }
}

刷新页面,点击按钮,奇怪的事情发生了,计数器的数字没有跟随按钮的点击而增加/减少。打日志进行排查也可以看到reducer已经执行了,store也已经更改了,但是页面上的计数器丝毫未变。

其实store变化了不假,但是页面没有变化,是因为没有刷新store.getState().value的值,这里需要触发重新render一下。

改进后的代码:

import React from 'react';
import store from './store';
import { addAction, decreseAction } from './action';

class App extends React.Component {
  
  addHandler = () => {
    store.dispatch(addAction());
  }

  decreseHandler = () => {
    store.dispatch(decreseAction());
  };

  // 页面挂载时添加一个store的订阅器,当store改变时,使用this.setState({})触发render
  componentDidMount(){
    store.subscribe(() => {
      this.setState({});
    })
  }

  render() {
    return (
      <div className="App">
        <button onClick={this.addHandler}>+</button>
        <button onClick={this.decreseHandler}>-</button>
        <div>{store.getState().value}</div>
      </div>
    ); 
  }
}

export default App;

再次刷新页面,可以看到,此时计数器就开始跟随按钮进行对应的加减操作了。

完整的项目地址

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值