redux与react-redux
前言
工作中Vue为主,React使用逐渐生疏,对redux仅达到会用的地步。偶尔遇到React项目,用到redux也就是把别人写过的东西重写一遍,最近有时间去B站白嫖了一下 React教程。写个笔记记录下!!!
一、redux和react-redux是啥?
redux是 JavaScript 状态容器,提供可预测化的状态管理,是一个独立的库可以搭配UI框架进行使用。
与redux稍加不同的是,react-redux是 React 的官方 Redux UI 绑定库,可以订阅 store、检查更新数据和触发重新渲染的过程可以变得更加通用和可复用。
简单的说,react-redux最终还是使用redux去取数据,不过是封装一层方便使用redux的api。
相对于Vuex的简单无脑操作,这两个算是稍微麻烦一点的。不过后续阿里封装了个hox,使用起来感觉简单了很多。
二、redux使用步骤
1.引入库
代码如下(示例):
npm install react-redux
2.原理图
原理图释义
1.所有的UI组件都应该包裹一个容器组件,他们是父子关系
2.容器组件是真正和redux打交道的,里面可以随意的使用redux的api
3.UI组件不能使用任何redux的api
4.容器组件会传给UI组件:1)redux中所保存的状态,2)用于操作状态的方法
5.备注:容器给UI传递:状态、操作状态的方法,均通过props传递
actions
1)含义
Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。
action返回的是Object类型的叫做同步action
action返回的是Function类型的叫做异步 action
异步action:
(1).明确:延迟的动作不想交给组件自身,想交给action
(2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回(非必须)
(3).具体编码:
1).cnpm i redux-thunk,并配置在store中,//行驶转换程序
2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务
3).异步任务有结果后,分发一个同步的action去真正的操作数据
(4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果再去分发同步action
2)demo
//redux文件夹下新建constant.js文件
/* 该模块适用于定义action对象中type类型的常量值
目的只有一个:便于管理的同时防止单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const SET_OPERATOR = 'setOperator'
//redux文件夹下新建action文件夹
//action下新建count_actions.js
/*
该文件专门为Count组件生成求和count_actions对象
*/
import {INCREMENT, DECREMENT} from '../constant'
export const incrementAction = (data) => ({ type: INCREMENT, data })//同步action
//同步action,就是指action的返回值为Object类型的一般对象
export function decrementAction(data){
return {
type: DECREMENT,
data
}//返回的是一个对象,普通数据类型,同步action,返回对象为异步
}
//异步action,就是指action的返回值为函数
//异步action中,一般都会调用同步action,异步action不是必须要用的
export const incrementAsyncAction = (data, time) => {
return (dispatch)=>{//返回对象为异步action
setTimeout(()=>{
dispatch(incrementAction(data))
}, time)
}
}
//action下新建user_action.js
import { SET_OPERATOR } from '../constant'
export function setOperator(data){
return{
type: SET_OPERATOR,
data,
}
}
store.js
1)含义
store是一个状态管理器,是一个仓库,存储公共的状态数据。Redux 应用只有一个单一的 store
1)引入redux中的createStore函数,创建一个store
2)createStore调用时要传入一个为其服务的reducer
3)记得暴露store对象
4) redux只负责管理状态,至于状态的改变驱动着页面的展示要靠我们自己写
2)demo
//redux文件夹新建store.js
//引入creacteStore,专门用于创建redux中最核心的store对象,applyMiddleware执行中间件
import { legacy_createStore as createStore, applyMiddleware} from "redux";
//引入redux-thunk用于支持异步action
import thunk from 'redux-thunk'
//引入为组件服务的reducer
import rootReducers from './reducers'
const store = createStore(rootReducers, applyMiddleware(thunk))
//暴露出去
export default store
reducer.js
1)含义
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
1)reducer的本质是一个函数,接收:preState,action,发布加工后的状态
2)reducer有两个作用:初始状态,加工状态
3)reducer被第一次调用时,是store自动触发的,传递的preState是undefined
2)demo
//redux文件夹下新建reducers文件夹
//新建count_reducer.js
import {INCREMENT, DECREMENT} from '../constant'
//监测提交过来的action,preState是前一条数据,初始化时默认为0
const counter = (preState = 0,action)=>{
const {type, data}= action
switch(type){
case INCREMENT:
return preState + data;
case DECREMENT:
return preState - data;
default:
return preState;
}
}
export default counter;
//redux文件夹下新建reducers文件夹
//新建user_reducer.js
import { SET_OPERATOR } from '../constant'
const operator = (preState = {}, action)=>{
const {type, data}= action
switch(type){
case SET_OPERATOR:
return Object.assign(preState, data);
default :
return preState
}
}
export default operator;
//redux文件夹下新建reducers文件夹
//新建index.js
import { combineReducers } from 'redux';
import counter from './count_reducer';
import operator from './user_reducer'
// 合并多个 reduce
const rootReducers = combineReducers({
counter:counter,
operator:operator
});
export default rootReducers;
Count.jsx
1)含义
操作组件,里面包含数据加减,和数据赋值两个功能
2)demo
//引入Count的UI组件
// import CountUI from '../../components/Count'
//引入action
import {
incrementAction,
decrementAction,
incrementAsyncAction
} from '../../redux/actions/count_actions'
//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'
// import store from '../../redux/store'
import React, { Component } from 'react'
import store from '../../redux/store'
import { setOperatorAction } from '../../redux/actions/user_action'
class Count extends Component {
state = {
}
//加
increment=()=>{
const {value}=this.selectNumber
this.props.increment(value*1)
}
//减
decrement=()=>{
const { value } = this.selectNumber
this.props.decrement(value*1)
}
//当前求和的数据为奇数再加
incrementIfOdd=()=>{
const counter = this.props.count.counter
const { value } = this.selectNumber
if(counter % 2 !=0) {
this.props.increment(value*1)
}
}
//异步加
incrementAsync=()=>{
const { value } = this.selectNumber
this.props.incrementAsync(value*1, 1000)
}
addUserInfo = () => {
const operator = {
name: '大黄'+Math.floor(Math.random()*100),
age: Math.floor(Math.random()*100)
}
this.props.setOperator(operator)
}
render() {
console.log(this.props)
return (
<div>
<h2>Person数组的长度{this.props.personLen.length}</h2>
<h1>当前求和为{this.props.count}</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>
)
}
}
//a函数的返回对象的key就作为传递给UI组件props的key,value就作为传递给UI组props的value---状态
const mapStateToProps = (state)=> ({count: state.counter, personLen: state.operator})
//b函数的返回对象的key就作为传递给UI组件props的key,value就作为传递给UI组props的value---操作状态的方法
//一般写法
// const mapDispatchToProps = (dispatch)=> ({
// increment: (number)=> dispatch(incrementAction(number)),
// decrement: (number)=> dispatch(decrementAction(number)),
// incrementAsync: (number)=> dispatch(incrementAsyncAction(number, 2000)),
// })
//高级写法
//因为用了react-redux能够自动dispatch
const mapDispatchToProps = {
increment: incrementAction,
decrement: decrementAction,
incrementAsync: incrementAsyncAction,
setOperator: setOperatorAction
}
//创建一个容器组件
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count)
//暴露容器组件
export default CountContainer;
//export default connect()(CountUI)
//App.js
import Count from './container/Count'
import Person from './container/Person'
// import store from './redux/store'
function App() {
return (
<div className="App">
<Count/>
<hr/>
<Person/>
</div>
);
}
export default App;
1)明确两个概念:
1)UI组件:不能使用redux的api,只负责页面的呈现和交互等
2)容器组件:负责和redux通信,将结果交给UI组件
2)如何创建一个容器组件---react-redux的connect函数
connect(mapStateToProps, mapDispatchToProps)(UI组件)
-mapStateToProps:映射状态,返回值是一个对象
-mapDispatchToProps:映射操作状态的方法,返回值是一个对象
3)备注:容器组件中的store是靠props穿进去的,而不是在容器组件中直接引入
Person.jsx
//引入Count的UI组件
// import CountUI from '../../components/Count'
//引入action
//引入connect用于连接UI组件与redux
import { connect } from 'react-redux'
// import store from '../../redux/store'
import React, { Component } from 'react'
import { setOperatorAction } from '../../redux/actions/user_action'
class Count extends Component {
state = {
}
addUserInfo = () => {
const name = this.nameNode.value
const age = this.ageNode.value
const personObj = {id: Math.floor(Math.random()*100), name, age}
this.props.setOperator(personObj)
this.nameNode.value = ''
this.ageNode.value = ''
console.log(name, age)
}
render() {
console.log(this.props.operator, this.props.count)
return (
<div>
<h2>Person组件,Count组件的总和为{this.props.count}</h2>
<div>
<input ref={c=> this.nameNode= c} type="text" placeholder="输入名字"/>
<input ref={c=> this.ageNode= c} type="text" placeholder="输入年龄"/>
<button onClick={()=>{this.addUserInfo()}}>添加用户信息</button>
<ul>
{
this.props.operator.map(item=>{
return <li key={item.id}>{item.name}---{item.age}</li>
})
}
</ul>
</div>
</div>
)
}
}
//a函数的返回对象的key就作为传递给UI组件props的key,value就作为传递给UI组props的value---状态
const mapStateToProps = (state)=> ({operator: state.operator, count: state.counter})
//高级写法
//因为用了react-redux能够自动dispatch
const mapDispatchToProps = {
setOperator: setOperatorAction
}
//创建一个容器组件
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count)
//暴露容器组件
export default CountContainer;
//export default connect()(CountUI)
1)定义一个Person组件,和Count组件通过redux共享数据
2)为Person组件编写,reducer、action,配置constant常量
3)重点:Person的reducer和Count的Reducer要使用combineReducer进行合并,合并后的总状态是一个对象
4)交给store的是总reducer,最后注意在组件中取出状态的时候,记得取到位
//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import store from './redux/store'
import App from './App';
import {Provider} from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
/*给容器组件传递store */
<Provider store={store}>
<App />
</Provider>
);
// 监听redux中状态的变化,如果redux的状态发生了变化,
//那么重新渲染App组件
//react-redux不用再监测了
// store.subscribe(() => {
// root.render(
// <Provider store={store}>
// <App />
// </Provider>
// );
// })
三、redux开发者工具的使用
npm i redux-devtools-extension
store文件
//引入redux-devtools-extension
import { composeWithDevTools } from 'redux-devtools-extension'
// const store = createStore(rootReducers, composeWithDevTools())
const store = createStore(rootReducers, composeWithDevTools(applyMiddleware(thunk)))
总结
1)容器组件和UI组件整合成一个文件
2)无需自己给容器组件传递store,给<App/>包裹 <Provider store={store}>即可
3)使用了react-redux无需自己检测redux中状态的变化,容器组件可以自动完成这个工作
4)mapDispatchToProps也可以简写成一个对象
5)一个组件要和redux打交道要经过几步?
1)定义好UI组件---不暴露
2)引入connect生成一个容器组件,并暴露,写法如下:
connect(state=>(
{key:value},//映射状态
),
{
key:action//映射操作状态的方法
})
在UI组件中通过this.props.***读取和操作状态