Redux梳理总结一文通透
第6章:redux
一、redux理解
1. 学习文档
- 英文文档: https://redux.js.org/
- 中文文档: http://www.redux.org.cn/
- Github: https://github.com/reactjs/redux
2. redux是什么
-
redux是一个专门用于做状态管理的JS库(不是react插件库)。
-
它可以用在react, angular, vue等项目中, 但基本与react配合使用。
-
作用: 集中式管理react应用中多个组件共享的状态。
-
安装:
npm i redux
3. 什么情况下使用redux
-
某个组件的状态,需要让其他组件可以随时拿到(共享)。
-
一个组件需要改变另一个组件的状态(通信)。
-
总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
4. redux工作流程
二、redux的三个核心概念
1. action
action
是store
中唯一的数据来源,一般来说,我们会通过调用store.dispatch
将 action 传到 store,我们需要传递的action
是一个对象,它必须要有一个type
值
-
动作的对象
-
包含两个属性
- type:标识属性,值为字符串,唯一,必要属性
- data:数据类型,值类型任意,可选属性
-
例子:
{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }
/* 该文件专门为Count组件生成action对象 */ import { INCREMENT, DECREMENT } from "./constant"; export const createIncrementAction = (data) => ({ type: INCREMENT, data }); export const createDecrementAction = (data) => ({ type: DECREMENT, data });
2. reducer
在 Reducer 中,我们需要指定状态的操作类型,要做怎样的数据更新,因此这个类型是必要的。reducer 会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state
-
用于初始化状态加工状态
-
根据旧的state和action, 产生新的state的纯函数。
-
作为createStore的参数
-
代码:
/* 1.该文件用于创建一个为Count组件服务的reducer,reducer的本质是一个函数 2.reducer会接受到两个参数,分别为之前的状态(preState),动作对象(action) */ import { INCREMENT, DECREMENT } from "./constant"; const initState = 0; // 初始化状态 export default function countReducer(preState = initState, action) { // console.log(preState); const { type, data } = action; switch (type) { case INCREMENT: // 加 return preState + data; case DECREMENT: // 减 return preState - data; default: return preState; } }
3. store
store 是 Redux 的核心,可以理解为是 Redux 的数据中台,我们可以将任何我们想要存放的数据放在 store中,在我们需要使用这些数据时,我们可以从中取出相应的数据。因此我们需要先创建一个 store,在 Redux 中可以使用 createStoreAPI 来创建一个store
在生产中,我们需要在
src
目录下的redux
文件夹中新增一个store.js
文件,在这个文件中,创建一个store
对象,并暴露它
-
将state、action、reducer联系在一起的对象
-
如何得到此对象?
import {createStore} from 'redux' import reducer from './reducers' const store = createStore(reducer)
-
此对象的功能?
-
getState()
: 得到state -
dispatch(action)
: 分发action, 触发reducer调用, 产生新的state -
subscribe(listener)
: 注册监听, 当产生了新的state时, 自动调用
-
-
代码:
/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ // 引入createStore import { legacy_createStore } from "redux"; // 引入为Count组件服务的reducer import countReducer from "./count_reducer"; // 暴露store export default legacy_createStore(countReducer);
三、redux核心API
1. createstore(reducer)
作用:创建包含指定reducer的store对象
2. store对象
-
作用: redux库最核心的管理对象
-
它内部维护着:
state
reducer
-
核心方法:
getState()
:获取当前时刻的store
dispatch(action)
:通过store
中的dispatch
方法来派生一个action
对象给store
subscribe(listener)
:subscribe
方法,这个方法可以帮助我们订阅store
的改变,只要store
发生改变,这个方法的回调就会执行
-
具体编码:
-
store.getState()
-
store.dispatch({type:'INCREMENT', number})
-
store.subscribe(render)
store.subscribe(() => { ReactDOM.render( < App /> , document.getElementById('root')) })
-
3. applyMiddleware()
作用:应用上基于redux的中间件(插件库)
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))
4. combineReducers()
作用:合并多个reducer函数
5. redux异步编程
-
redux默认是不能进行异步处理的,
-
某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
-
使用异步中间件
npm install --save redux-thunk
import thunk from 'redux-thunk' export default createStore(countReducer, applyMiddleware(thunk))
-
代码:
export const createIncrementAsyncAction = (data, time) => { // 无需引入 store ,在调用的时候是由 store 调用的 return (dispatch) => { setTimeout(() => { dispatch(createIncrementAction(data)) }, time) } }
-
注意:异步 action 不是必须要写的,完全可以自己等待异步任务的结果后再去分发同步action
-
采用
react-thunk
能让异步代码像同步代码一样执行,在redux
中我们也是可以实现异步的,但是这样我们的代码中会有很多异步的细节,这不是我们想看到的,利用react-thunk
之类的库,就能让我们只关心我们的业务
6. Redux 三大原则
理解好 Redux 有助于我们更好的理解接下来的 React -Redux
-
第一个原则
-
单向数据流:整个 Redux 中,数据流向是单向的
-
UI 组件 —> action —> store —> reducer —> store
-
-
第二个原则
- state 只读:在 Redux 中不能通过直接改变 state ,来控制状态的改变,如果想要改变 state ,则需要触发一次 action。通过 action 执行 reducer
-
第三个原则
- 纯函数执行:每一个reducer 都是一个纯函数,不会有任何副作用,返回是一个新的 state,state 改变会触发 store 中的 subscribe###
四、react-redux
在前面我们学习了 Redux ,我们在写案例的时候,也发现了它存在着一些问题,例如组件状态无法公用,每一个状态组件都需要通过订阅来监视,状态更新会影响到全部组件更新,面对着这些问题,React 官方在 redux 基础上提出了 React-Redux 库,专门用来简化react应用中使用redux
安装:
npm i react-redux
1. 容器组件和 UI 组件
-
UI组件
-
只负责 UI 的呈现,不带有任何业务逻辑
-
通过props接收数据(一般数据和函数)
-
不使用任何 Redux 的 API
-
一般保存在components文件夹下
-
-
容器组件
-
负责管理数据和业务逻辑,不负责UI的呈现
-
使用 Redux 的 API
-
一般保存在containers文件夹下
要实现容器组件和 UI 组件的连接,我们需要通过connect
来实现
-
2. Provider
由于我们的状态可能会被很多组件使用,所以 React-Redux 给我们提供了一个 Provider 组件,可以全局注入 redux 中的 store ,只需要把 Provider 注册在根部组件即可
在 src 目录下的 index.js
文件中,引入 Provider
,直接用 Provider
标签包裹 App
组件,将 store
写在 Provider
中即可。这样我们在 App.jsx
文件中,组件无需手写指定 store
,即可使用 store
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
3. connect
在前面我们看到的 react-redux 原理图时,我们会发现容器组件需要给 UI 组件传递状态和方法,并且是通过
props
来传递,看起来很简单。但是,我们会发现容器组件中似乎没有我们平常传递props
的情形。因为UI组件是通过**connect()(UI组件)**这样的形式连接。这时候就需要继续研究一下容器组件中的唯一一个函数
connect
-
connect 方法是一个连接器,用于连接容器组件和 UI 组件的高阶函数,它第一次执行时,接收4个参数,这些参数都是可选的,第一次执行时的四个参数:
mapStateToProps
、mapDispatchToProps
、mergeProps
、options
,它执行的执行的结果还是一个函数,第二次执行接收一个 UI 组件参数 -
mapStateToProps
:函数建立了UI 组件和容器组件间的状态传递
-
代码:
const mapStateToProps = state => ({ count: state })
-
它接收
state
作为参数,并且返回一个对象,这个对象标识着 UI 组件的同名参数 -
返回的对象中的 key 就作为传递给 UI 组件 props 的 key,value 就作为 props 的 value
-
UI 组件中直接通过 props 来读取
count
值
<h1>当前求和为:{this.props.count}</h1>
-
-
mapDispatchToProps
:函数或者对象-
connect 接受的第二个参数是
mapDispatchToProps
,它是用于建立 UI 组件的参数到store.dispacth
方法的映射 -
我们可以把参数写成对象形式,在这里面定义 action 执行的方法,例如
jia
执行什么函数,jian
执行什么函数? -
我们都可以在这个参数中定义,如下定义了几个方法对应的操作函数
{ jia: createIncrementAction, jian: createDecrementAction, jiaAsync: createIncrementAsyncAction }
-
自动调用dispatch
-
似乎少了点什么,我们在这里调用了函数,创建了
action
对象,但是好像store
并没有执行dispatch
,那是不是断了呢?执行不了呢? -
其实这里
react-redux
已经帮我们做了优化,当调用actionCreator
的时候,会立即发送action
给store
而不用手动的dispatch
-
-
-
即
connect(mapStateToProps, mapDispatchToProps)(CountUI);
4. react-redux的优化
-
容器组件和UI组件整合一个文件
-
无需自己给容器组件传递store,给
<App/>
包裹一个<Provider store={store}>
即可。 -
使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。即不用写一下代码:
// 监测redux中状态的改变,如果redux的状态发生了改变,那么重新渲染App组件 store.subscribe(() => { ReactDOM.render(<App />, document.getElementById("root")); });
-
mapDispatchToProps也可以简单的写成一个对象
-
一个组件要和redux“打交道”要经过哪几步?
-
定义好UI组件—不暴露
-
引入connect生成一个容器组件,并暴露,写法如下:
connect( state => ({ key : value }), //映射状态 { key : xxxxxAction } //映射操作状态的方法 )(UI组件)
-
在UI组件中通过this.props.xxxxxxx读取和操作状态
-
5. react-redux数据共享
-
定义一个Pserson组件,和Count组件通过redux共享数据。
-
为Person组件编写:reducer、action,配置constant常量。
-
重点:Person的reducer和Count的Reducer要使用combineReducers进行合并, 合并后的总状态是一个对象!!!
// 暴露store export default legacy_createStore( combineReducers({ count: countReducer, persons: personReducer }), applyMiddleware(thunk) );
-
交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
export default connect( (state) => ({ personList: state.persons, count: state.count }), { addPerson: createAddPersonAction, } )(Person);
-
优化:reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer
6. react-redux开发者工具的使用
-
下载Chrome浏览器插件
地址:Redux-DevTools 浏览器调试工具 -
在项目中安装redux-devtools-extension
npm i redux-devtools-extension
-
在store中进行配置
// 引入redux-devtools-extension import { composeWithDevTools } from "redux-devtools-extension"; // 暴露store export default legacy_createStore( combineReducers({ count: countReducer, persons: personReducer }), composeWithDevTools(applyMiddleware(thunk)) );
-
成功配置如下
7. 纯函数
-
如果函数的调用参数相同, 则永远返回相同的结果. 它不依赖于程序执行期间函数外部任何状态或数据的变化, 只依赖于传入的参数
-
必须遵守以下一些约束
-
不得改写参数数据
-
不会产生任何副作用,例如网络请求,输入和输出设备
-
不能调用Date.now()或者Math.random()等不纯的方法
-
-
redux的reducer函数必须是一个纯函数
本文来自博客园,作者:你就是星光,转载请注明原文链接:https://www.cnblogs.com/xzqyl/p/17045385.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析