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)
相关推荐
gqkmiss20 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃25 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰30 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye36 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm38 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You2 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生2 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互