系列文章目录
第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一)
第二章:React基础知识(组件实例三大核心属性state、props、refs)(二)
第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
第四章:React脚手架应用(创建脚手架、代理配置、ajax相关、组件通信)(四)
第五章:react-router5路由相关一(路由相关概念、基本使用、NavLink与NavLink的封装、Switch的使用、严格匹配、路由重定向、路由组件与一般组件的区别)(五)
第六章:react-router5路由相关二(嵌套路由、路由传参、replace、编程式路由导航、withRouter的使用、BrowserRouter与HashRouter的区别)(六)
第七章:React-Router6路由相关一(路由的基本使用、重定向、NavLink·、路由表、嵌套路由)(七)
第八章:React-Router6路由相关二(路由传参、编程式路由导航、路由相关hooks)(八)
第九章:React相关扩展一(setState、lazyLoad、Hooks相关)(九)
第十章:React相关扩展二(Fragment、Content、组件优化、render props、错误边界)(十)
文章目录
- 系列文章目录
- 一、什么是Redux
- 1.1 概念
- 1.2 何时使用?
- 1.3 redux的工作原理
- 二、核心概念
- 2.1 state
- 2.2 action
- 2.3 reducer(重要)
- 2.4 store
- 2.5 dispatch
- 2.6 subscribe(listener)
- 三、redux的基本使用(搭建redux环境)
一、什么是Redux
1.1 概念
Redux
是 JavaScript应用的状态容器
,提供可预测的状态管理。可以开发出行为稳定可预测的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux
是一个使用叫做“action”
的事件来管理和更新应用状态的模式和工具库
,它以集中式Store的方式对整个应用中使用的状态进行集中管理
,其规则确保状态只能以可预测的方式更新。Redux
提供的模式和工具使您更容易理解应用程序中的状态何时、何地、为什么以及如何更新
,以及当这些更改发生时您的应用程序逻辑将如何表现。- 类似于vuex 但是不同于vuex,可以对状态进行管理。
1.2 何时使用?
- 在应用的大量地方,都存在大量的状态
- 应用状态会随着时间的推移而频繁更新
- 更新该状态的逻辑可能很复杂
- 中型和大型代码量的应用,很多人协同开发
- 不同身份的⽤户有不同的使⽤⽅式(⽐如普通⽤户和管理员)
- 与服务器⼤量交互,或者使⽤了WebSocket
- View要从多个来源获取数据
- 某个组件的状态,需要共享某个状态需要在任何地⽅都可以拿到
- ⼀个组件需要改变全局状态
- ⼀个组件需要改变另⼀个组件的状态
1.3 redux的工作原理
-
页面初始渲染时:
- 使用最顶层的 root reducer 函数创建 Redux store
store 调用一次 root reducer
,并将返回值保存为它的初始 state
- 当 UI 首次渲染时,
UI 组件访问 Redux store 的当前 state
,并使用该数据来决定要呈现的内容。 - 同时
监听store 的更新
,以便他们可以知道 state 是否已更改。
-
页面更新渲染时:
dispatch
一个action
到Redux store
,例如dispatch({type:'counter/increment'})
store
用之前的 state
和当前的 action 再次运行 reducer 函数
,并将返回值保存为新的 state
store
通知所有订阅过的 UI
,通知它们 store 发生更新
- 每个订阅过 store 数据的 UI 组件都会检查它们需要的 state 部分是否被更新。
发现数据被更新
的每个组件都强制使用新数据重新渲染
,紧接着更新网页
。
二、核心概念
应用的整体全局状态
以对象树的方式存放于单个 store
。 唯一改变状态树的方法
是创建 action
,一个描述发生了什么的对象,并将其 dispatch 给 store
。 要指定状态树如何响应 action 来进行更新
,你可以编写纯 reducer 函数
,这些函数根据旧 state 和 action 来计算新 state
。
2.1 state
- 托管给redux管理的状态
- 示例代码:
let state = {
todos:[],
params:{}
}
2.2 action
-
Action
Action
描述当前发⽣的事情。改变State的唯⼀办法
,就是使⽤Action。它会运送数据到 Store。- Action 本质上是 JavaScript 普通对象。
action
内必须使⽤
⼀个字符串类型的type字段
来表示将要执⾏的动作
。 - type 字段是一个字符串,给这个 action 一个描述性的名字,比如"todos/todoAdded"。
- action对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为data的字段中,也可放放到其他属性中,不过最好放到data中。
- 示例代码:
{ type: 'ADD_TODO', text: '去游泳馆' } { type: 'TOGGLE_TODO', index: 1 } { type: 'SET_VISIBILITY_FILTER', filter: 'completed' }
-
Action Creator
-
action creator
是一个创建并返回一个 action 对象的函数
。它的作用是让你不必每次都手动编写 action对象 -
示例代码:
const addTodo = data => ({type: 'todos/todoAdded',data})
-
2.3 reducer(重要)
-
reducer
是一个函数
,接收当前的state和一个action 对象
,必要时决定如何更新状态,并返回新状态。函数签名是:(state, action) => newState
。可以将reducer 视为一个事件监听器,它根据接收到的action(事件)类型处理事件。 -
Reducer 必需符合以下规则:
仅使用 state 和 action 参数计算新的状态值
禁止直接修改 state
。必须通过复制现有的 state 并对复制的值进行更改的方式来做不可变更新禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码
-
reducer 函数内部的逻辑通常遵循以下步骤:
- 检查 reducer 是否联系这个 action
- 如果是,则复制 state,使用新值更新 state 副本,然后返回新 state
- 否则,返回原来的 state 不变
- 检查 reducer 是否联系这个 action
-
示例代码:
// 初始化状态 const initialState = { value: 0, index: 1 } function counterReducer(preState=initialState, action) { //从action对象中获取:type、data const {type,data} = action //根据type决定如何加工数据 switch (type) { case 'increment': //如果是加 return { ...preState value:preState.value + data } case 'decrement': //若果是减 return { ...preState value:preState.value - data } default: // 返回prestate 不变 return preState } }
2.4 store
-
当前
Redux 应用的状态存在于
一个名为store
的对象中。store
是通过传入一个reducer来创建的
,并且有一个getState 的方法
,它返回当前状态值
。 -
Store
就是保存数据的地⽅,可以把它看成⼀个容器。整个应⽤只能有⼀个Store。Redux提供createStore
这个函数,⽤来⽣成Store
。 -
示例代码:
//引入createStore,专门用于创建redux中最为核心的store对象 // 旧版本的引入 // import {createStore} from 'redux' // 新版本的引入 import { legacy_createStore as createStore } from 'redux' //引入为Count组件服务的reducer import reducer from './reducer' //暴露store export default createStore(reducer) /* 如果该仓库用多个reducer,则使用combineReducers函数来合并reducer, 可以通过combineReducers组合多个Reducer,然后通过createStore来创建状态机。*/ const store2=createStore(combineReducers({ todos, counter, }));
或者直接使用@reduxjs/toolkit包中的configureStore方法来生成store
import { configureStore } from '@reduxjs/toolkit' const store = configureStore({ reducer})
仓库创建好了之后,将store引入到需要使用仓库的组件中。
import store from './store/index.js' // 分发动作 store.dispatch({type:'',data:''}) // 获取仓库数据 store.getState();
2.5 dispatch
-
Redux store 有一个方法叫
dispatch
。更新 state 的唯一方法
是调用 store.dispatch() 并传入一个action对象
。store 将执行所有reducer函数并计算出更新后的state,调用 getState()
可以获取新state。 -
示例代码:
store.dispatch({ type: 'counter/increment' }) console.log(store.getState()) // {value: 1}
-
dispatch 一个 action 可以形象的理解为 "触发一个事件"
。发生了一些事情,我们希望 store
知道这件事。Reducer 就像事件监听器一样,当它们收到关注的 action 后,它就会更新 state 作为响应。我们通常调用 action creator 来调用 action
: -
示例代码:
const increment = () => { return { type: 'counter/increment' } } // 分发动作 store.dispatch(increment()) console.log(store.getState())
2.6 subscribe(listener)
-
添加一个变化监听器
。每当dispatch action 的时候就会执行
,state
树中的一部分可能已经变化。你可以在回调函数里调用 getState() 来拿到当前 state
。你可以在变化监听器里面进行
dispatch(),如果需要解绑这个变化监听器,执行 subscribe 返回的函数即可。 -
参数listener (Function): 每
当 dispatch action 的时候都会执行的回调
。state 树中的一部分可能已经变化。你可以在回调函数里调用 getState() 来拿到当前 state。 -
返回值(Function): 一个可以解绑变化监听器的函数。
// 监听方法一 store.subscribe(() => { this.setState({ ...store.getState() }); }); //订阅者做的事情 // 监听方法二 store.subscribe(()=>{ ReactDOM.render(<App/>,document.getElementById('root')) })
三、redux的基本使用(搭建redux环境)
-
安装
npm install redux
-
主要API
createStore(reducer, [preloadedState], [enhancer])
创建仓库combineReducers(reducers)
合并reducerapplyMiddleware(...middlewares)
应用中间件
-
store相关API
store.getState()
获取仓库中的状态store.dispatch(action)
分发到某个动作store.subscribe(listener)
订阅仓库内状态更新replaceReducer(nextReducer)
替换reducer
-
代码案例片段:
index.jsimport React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' ReactDOM.render(<App/>,document.getElementById('root')) store.subscribe(()=>{ ReactDOM.render(<App/>,document.getElementById('root')) })
App.js
import React, { Component } from 'react' import Count from './components/Count' export default class App extends Component { render() { return ( <div> <Count/> </div> ) } }
redux/count_action.js(该文件专门为Count组件生成action对象)
//同步action,就是指action的值为Object类型的一般对象 export const createIncrementAction = data => ({type:'increment',data}) export const createDecrementAction = data => ({type:'decrement',data}) //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。 export const createIncrementAsyncAction = (data,time) => { return (dispatch)=>{ setTimeout(()=>{ dispatch(createIncrementAction(data)) },time) } }
redux/count_reducer.js
/* 1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数 2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action) */ const initState ={value:0 ,index:1} //初始化状态 export default function countReducer(preState=initState,action){ // console.log(preState); //从action对象中获取:type、data const {type,data} = action //根据type决定如何加工数据 switch (type) { case 'increment': //如果是加 return preState + data case 'decrement': //若果是减 return preState - data default: return preState } }
redux/store.js
/* 该文件专门用于暴露一个store对象,整个应用只有一个store对象 */ //引入createStore,专门用于创建redux中最为核心的store对象 // 旧版 // import { createStore } from 'redux' //新版 import { legacy_createStore as createStore, applyMiddleware } from 'redux' //引入为Count组件服务的reducer import countReducer from './count_reducer' //引入redux-thunk,用于支持异步action import thunk from 'redux-thunk' //暴露store export default createStore(countReducer, applyMiddleware(thunk))
components/Count.jsx
import React, { Component } from 'react' //引入store,用于获取redux中保存状态 import store from '../../redux/store' //引入actionCreator,专门用于创建action对象 import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' export default class Count extends Component { state = {carName:'奔驰c63'} /* componentDidMount(){ //检测redux中状态的变化,只要变化,就调用render store.subscribe(()=>{ this.setState({}) }) } */ //加法 increment = ()=>{ const {value} = this.selectNumber store.dispatch(createIncrementAction(value*1)) } //减法 decrement = ()=>{ const {value} = this.selectNumber store.dispatch(createDecrementAction(value*1)) } //奇数再加 incrementIfOdd = ()=>{ const {value} = this.selectNumber const count = store.getState() if(count % 2 !== 0){ store.dispatch(createIncrementAction(value*1)) } } //异步加 incrementAsync = ()=>{ const {value} = this.selectNumber // setTimeout(()=>{ store.dispatch(createIncrementAsyncAction(value*1,500)) // },500) } render() { return ( <div> <h1>当前求和为:{store.getState()}</h1> <select ref={c => this.selectNumber = c}> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
运行结果: