redux的介绍、安装、三大核心与执行流程
- 一、redux的基本介绍
- 二、redux的安装
- 三、redux核心概念
- 3.1 action
- 3.2 reducer
- 3.3 store
- 四、Redux代码执行流程
- 五、加减案例练习
一、redux的基本介绍
- redux中文官网
Redux 是 React 中最常用的状态管理工具(状态容器)
React 只是 DOM 的一个抽象层(UI 库),并不是 Web 应用的完整解决方案。因此 React 在涉及到数据的处理以及组件之间的通信时会比较复杂
不使用redux 与 使用redux的区别 (组件之间的通讯问题)
不使用redux
1.只能使用父子组件通讯、状态提升等 React 自带机制
2.处理远房亲戚(非父子)关系的组件通讯时乏力
3.组件之间的数据流混乱,出现 Bug 时难定位
使用redux
1.集中式存储和管理应用的状态
2.处理组件通讯问题时,无视组件之间的层级关系
3.简化大型复杂应用中组件之间的通讯问题
4.数据流清晰,易于定位 Bug
二、redux的安装
npm i redux
三、redux核心概念
为了让代码各部分职责清晰、明确,Redux 代码被分为三个核心概念:action/reducer/store
action -> reducer -> store
action(动作):描述要做的事情
reducer(函数):更新状态
store(仓库):整合 action 和 reducer
类比生活中的例子来理解三个核心概念:
1.action:相当于公司中要做的事情,比如软件开发、测试,打扫卫生等
2.reducer:相当于公司的员工,负责干活的
3.store:相当于公司的老板
4.流程:老板(store)分配公司中要做的事情(action)给员工(reducer),员工干完活把结果交给老板
3.1 action
action:描述要做的事情,项目中的每一个功能都是一个 action
计数器案例:计数器加1、减1
购物车案例:获取购物车数据、切换商品选中状态
项目:登录,退出等
特点
只描述做什么
JS 对象,必须带有 type 属性,用于区分动作的类型
根据功能的不同,可以携带额外的数据(比如,payload 有效载荷,也就是附带的额外的数据),配合该数据来完成相应功能
核心代码 (原生html中使用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入redux -->
<script src="./node_modules/redux/dist/redux.js"></script>
</head>
<body>
<div>1</div>
<button>+1</button>
<button>-1</button>
</body>
<script>
//1.action 是一个函数 必须有type属性
//1.1原始创建
// const action={
// type:'ADD'
// }
//1.2 动态action 函数创建
// const Add = () => {
// return {
// type: 'ADD',
// }
// }
//简写 并传参 使用()包裹对象
const Add = (id) => ({ type: 'ADD', id })
console.log('action', Add(3))
</script>
</html>
3.2 reducer
reducer:函数,用来处理 action 并更新状态,是 Redux 状态更新的地方
特点
函数签名为:(prevState, action) => newState
接收上一次的状态和 action 作为参数,根据 action 的类型,执行不同操作,最终返回新的状态
注意:该函数一定要有返回值,即使状态没有改变也要返回上一次的状态
约定:reducer 是一个纯函数,并且不能包含 side effect 副作用(比如,不能修改函数参数、不能修改函数外部数据、不能进行异步操作等)
纯函数:相同的输入总是得到相同的输出
1.不要直接修改参数 state 的值(也就是:不要直接修改当前状态,而是根据当前状态值创建新的状态值)
2.不要使用 Math.random() / new Date() / Date.now() / ajax 请求等不纯的操作
3.不要让 reducer 执行副作用(side effect)
核心代码 (原生html中使用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入redux -->
<script src="./node_modules/redux/dist/redux.js"></script>
</head>
<body>
<div>1</div>
<button>+1</button>
<button>-1</button>
</body>
<script>
//reducer 接收两个参数 必须有返回值 必须是纯函数
//参数1 上一次的状态
//参数2 action
//累加
const Add = (num) => ({ type: 'ADD', num })
//类减
const Sub = (num) => ({ type: 'SUB', num })
//reducer
// const reducer = (state, action) => {
// return 'reducer返回'
// }
const reducer = (state = 0, action) => {
//使用switch case
switch (action.type) {
case 'ADD':
return state + 1
case 'SUB':
return state - 1
default:
return state
}
}
console.log('减法', reducer(1, Sub()))
console.log('加法', reducer(1, { type: 'ADD' }))
</script>
</html>
3.3 store
store:仓库,Redux 的核心,整合 action 和 reducer
特点
一个应用只有一个 store
维护应用的状态,获取状态:store.getState()
发起状态更新时,需要分发 action:store.dispatch(action)
创建 store 时接收 reducer 作为参数:const store = createStore(reducer)
订阅(监听)状态变化:const unSubscribe = store.subscribe(() => {})
取消订阅状态变化: unSubscribe()
核心代码 (原生html中使用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入redux -->
<script src="./node_modules/redux/dist/redux.js"></script>
</head>
<body>
<div>1</div>
<button>+1</button>
<button>-1</button>
</body>
<script>
//累加
const Add = (num) => ({ type: 'ADD', num })
//类减
const Sub = (num) => ({ type: 'SUB', num })
function reducer(state = 100, action) {
//使用switch case
switch (action.type) {
case 'ADD':
return state + 1
case 'SUB':
return state - 1
default:
return state
}
}
//因为不是es6 引入 全局有一个window.Redux
console.log('window.Redux', window.Redux)
//解构 createStore
const { createStore } = window.Redux
console.log('createStore', createStore)
//创建store 参数一必传 (reducer)
const store = createStore(reducer)
console.log('store', store)
//dispatch getState subscribe
//1.获取redux中的数据
store.getState()
console.log('store.getState', store.getState())
//2.订阅:只要state发生变化 这个订阅的回调函数 就会执行
store.subscribe(() => {
console.log('订阅', store.getState())
})
//3.发起状态更新时,需要分发 action
store.dispatch(Add())
console.log('store.getState', store.getState())
store.dispatch(Sub())
console.log('store.getState', store.getState())
</script>
</html>
四、Redux代码执行流程
1.创建 store 时,Redux 就会先调用一次 reducer,来获取到默认状态
2.分发动作 store.dispatch(action)更新状态
3.Redux store 调用 reducer 传入:上一次的状态(当前示例中就是:10)和 action({ type: 'increment' }),计算出新的状态并返回
4.reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕
import { createStore } from 'redux'
const store = createStore(reducer)
// reducer(10, { type: 'increment' })
function reducer(state = 10, action) {
console.log('reducer:', state, action)
switch (action.type) {
case 'increment':
return state + 1
default:
return state
}
}
console.log('状态值为:', store.getState()) // 10
// 发起更新状态:
// 参数: action 对象
store.dispatch({ type: 'increment' })
// 相当于: reducer(10, { type: 'increment' })
console.log('更新后:', store.getState()) // 11
五、加减案例练习
准备两个按钮 点击加号按钮 数值+1 点击减号按钮 数值-1
实现代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入redux -->
<script src="./node_modules/redux/dist/redux.js"></script>
</head>
<body>
<div>1</div>
<button class="add">+1</button>
<button class="sub">-1</button>
</body>
<script>
//第一步 创建action
//加法
const Add = (num) => ({ type: 'ADD', num })
//减法
const Sub = (num) => ({ type: 'SUB', num })
//第二步 创建 reducer
const reducer = (state = 100, action) => {
switch (action.type) {
case 'ADD':
return state + 1
case 'SUB':
return state - 1
default:
return state
}
}
//第三步 引入store
//3.1 解构出store 并传递reducer
const { createStore } = window.Redux
const store = createStore(reducer)
//3.2 store.getState获取redux中的数据 初始原始值
document.querySelector('div').innerHTML = store.getState()
//3.3 store.subscribe 订阅获取state变化
store.subscribe(() => {
console.log('值发生变化', store.getState())
document.querySelector('div').innerHTML = store.getState()
})
//3.4 绑定点击事件 并调用store.dispatch 分发action
document.querySelector('.add').onclick = function () {
store.dispatch(Add())
}
document.querySelector('.sub').onclick = function () {
store.dispatch(Sub())
}
</script>
</html>