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-thunkimport 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

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号