React--Redux①(简介与基本使用)

为什么需要Redux

  • 随着 JavaScript 单页应用日趋复杂,需要管理的状态或事件处理函数也越来越多,而且逐渐难以管理
  • 其中包括服务器返回的数据、缓存数据、用户操作产生的数据 等,也包括一些 UI 的状态 ,比如某些元素是否被选中,是否显示加载动效,当前分页

管理不断变化的 state 的痛点

  • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,视图可能也会引起相应变化
  • 当应用程序复杂时,state 中状态的变化时机、原因、过程等,将变得非常难以控制和追踪
  • React 组件通信的数据流是单向的,不能自下而上传递数据,单向数据流撑起 React 的数据可控性

React 的状态维护问题

  • React 虽然在视图层解决了 DOM 的渲染过程,但无论是组件自身的 state,还是组件通信的 props 传递,或是 context 数据的共享,依然是留给开发者来管理
  • React 是视图层框架,主要负责管理视图statepropscontext 如何维护最终由开发者决定

Redux 是什么?

  • Redux一个用于管理数据和 UI 状态 的 JavaScript 应用工具,提供了可预测的状态管理
  • Redux 除了和 React 一起使用之外,也能和其他框架搭配使用(如 Vue),并且体积非常小(只有 2kb)

使用 Redux 的目的:

  • 为了便于开发者管理不断变化的 state ,所以需要 Redux 来对 state 的状态进行管理
  • 使用 React 就是将状态进行集中管理,降低状态管理难度

核心理念

Redux 的三个核心理念: StoreActionReducer

  • Store: 集中维护状态的仓库,用于存放数据
  • Action: 普通的 JavaScript 对象,用于描述更新的类型和内容,所有数据变化必须通过派发(dispatch) action 来更新,这样可清晰得知数据如何变化,所有数据的变化都是可跟追、可预测的
  • Reducer: 必须是纯函数,用于将传入的 stateaction 结合起来生成一个新的 state

三大原则

保持单一数据源

  • 整个应用程序的 state 被存储在一棵对象树中,并且对象树只存储在 store
  • Redux 并没有强制不能创建多个 store,但是多个 store 不利于数据维护
  • 单一数据源可让整个应用程序的 state 变得方便维护、追踪、修改

state 是只读的

  • 修改 state 的唯一方法是触发 action ,不要试图通过其他方式修改 state
  • 确保视图或网络请求都不能直接修改 state,只能通过 action 来描述该如何修改 state
  • 保证所有修改都被集中化处理,并且按照严格的顺序执行,不需要担心竟态问题

使用纯函数来执行修改

  • 通过 reducer 将旧 stateactions 联系在一起,并且返回一个新的 state
  • 随着项目复杂度增加,可将 reducer 拆分成多个小的 reducers,分别操作 state 的不同部分
  • 所有的 reducer 都应该是纯函数,不能产生任何的副作用

使用流程

  • 安装 Redux
shell 复制代码
 npm i redux
  • 创建初始的 state 对象,用于保存状态,存放于 reducer 集中管理
javascript 复制代码
 // reducer.js
 // 初始化的数据
 const initialState = {
   name: 'James',
   age: 38
 }
 ​
 function reducer(state = initialState, action) {
   return state;
 }
 ​
 module.exports = reducer;
  • 创建 store 存储 state,必须添加 reducer
javascript 复制代码
 // store/index.js
 const { createStore } = require('redux');
 const reducer = require('./reducer');
 ​
 // 创建store
 const store = createStore(reducer);
 ​
 module.exports = store;
  • 创建 actionCreators,用于生成派发的 action 对象
javascript 复制代码
 // actionCreators.js
 const { CHANGE_AGE, CHANGE_NAME } = require('./constants');
 ​
 // actionCreators:用于生成action
 const changeNameAction = (name) => ({
   type: CHANGE_NAME, // 通常action中都会有type属性
   name
 })
 ​
 const changeAgeAction = (age) => ({
   type: CHANGE_AGE, 
   age
 })
 ​
 module.exports =  {
   changeNameAction,
   changeAgeAction
 }
javascript 复制代码
 // constants.js
 // 用于保存不变的action.type字段
 const CHANGE_AGE = 'change_age'
 const CHANGE_NAME = 'change_name'
 ​
 module.exports = {
   CHANGE_AGE,
   CHANGE_NAME
 }
  • 修改 reducer 中的处理代码,reducer 必须是纯函数,不能直接修改 state
javascript 复制代码
 const { CHANGE_AGE, CHANGE_NAME } = require('./constants')
 ​
 /** 
 * @param {Object} state store在当前保存的state
 * @param {Object} action 本次需要更新的action
 * @return {Object} 作为store之后存储的state
 */
 function reducer(state = initialState, action) {
   switch(action.type) {
     // 若有新数据更新,则返回新的state
     case CHANGE_NAME:
       return {...state, name: action.name }
     case CHANGE_AGE:
       return {...state, age: action.age }
     default:
       // 若无数据更新,则返回之前的state
       return state
   }
 }
  • 可以在派发 action 之前,监听 store 的变化,可通过 store.getState() 来获取当前的 state
javascript 复制代码
 // 订阅store
 const unsubscribe = store.subscribe(() => {
   console.log('订阅数据变化:',store.getState());
 })
 ​
 // 派发action,修改store中的数据,当调用dispatch时就会触发reducer中switch的逻辑
 store.dispatch(changeNameAction('Jimmy'))
 ​
 // 取消订阅
 unsubscribe()

结合React使用

  • React 中使用 store,每个组件都有可能用到,如果在每个组件中都引入 store 去进行操作,那么代码重复率和耦合度就会很高
  • 使用第三方库 react-redux,可以通过高阶组件的原理对 store 的逻辑进行抽取
  • 安装 react-redux
shell 复制代码
 npm i react-redux
  • react-redux 内部提供了一个 Provider 组件,可以将 store 共享到所有组件
jsx 复制代码
 import { Provider } from 'react-redux';
 import store from './store';
 ​
 // 将store共享到App根组件
 <Provider store={store}>
   <App />
 </Provider>
  • 在需要使用 store 的组件中,通过 react-redux 内部提供的 connect 函数,将 store 关联起来

connect 函数接收两个参数,返回一个高阶组件

  • 参数① mapStateToProps 函数,用于将 state 中的数据注入到组件的 props
  • 参数② mapActionToProps 函数,用于将 action 映射到组件的 props
jsx 复制代码
 import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { mulCountAction } from '../store/actionCreators';
 ​
 class About extends PureComponent {
   render() {
     return (
       <div>
         <h2>About Count:{this.props.count}</h2>
         <div className='button-box'>
           <button onClick={e => this.props.mulCount(2)}>x2</button>
           <button onClick={e => this.props.mulCount(5)}>x5</button>
           <button onClick={e => this.props.mulCount(10)}>x10</button>
         </div>
       </div>
     )
   }
 }
 ​
 const mapStateToProps = (state) => ({
   count: state.count // 将state.count映射到this.props.count
 })
 ​
 const mapActionToProps = (dispatch) => ({
   mulCount: (count) => {
     dispatch(mulCountAction(count)) // 将分发action映射到this.props.mulCount
   }
 })
 ​
 export default connect(mapStateToProps, mapActionToProps)(About)
相关推荐
C澒6 小时前
多场景多角色前端架构方案:基于页面协议化与模块标准化的通用能力沉淀
前端·架构·系统架构·前端框架
崔庆才丨静觅6 小时前
稳定好用的 ADSL 拨号代理,就这家了!
前端
江湖有缘6 小时前
Docker部署music-tag-web音乐标签编辑器
前端·docker·编辑器
早點睡3906 小时前
高级进阶 React Native 鸿蒙跨平台开发:react-native-device-info 设备信息获取
react native·react.js·harmonyos
恋猫de小郭7 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅14 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606114 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅15 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端