一、 Redux Toolkit ★★★
本文知识是对前两章文章的内容的简写方式,前两章是redux的基础使用。
@reduxjs/toolkit包仅仅是对redux的代码进行封装;在react中使用还是需要用react-redux包将两者联系在一起
npm install @reduxjs/toolkit react-redux
- Redux Toolkit的核心API主要是如下几个
configureStore
:包装createStore以提供简化的配置选项和良好的默认值。它可以自动组合你的 slice reducer,添加你提供的任何 Redux中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。createSlice
: 接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions.createAsyncThunk
: 接受一个动作类型字符电和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的 thunk
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
reducer: {}
})
1.1 createSlice
createSlice
接收一个包含三个主要选项字段的对象:
createSlice返回值是一个对象,包含所有的actions
name
:用户标记slice的名词字符串,将用作生成的 action types 的前缀initialState
:reducer 的初始 state初始化值reducers
:相当于reducer函数- 对象类型,并且可以添加很多的函数
- 函数类似于redux原来reducer中的一个case语句
- 函数的参数
- 参数一:state
- 参数二:调用这个action时,传递的action参数;
1.2.configureStore
用于创建store对象,常见参数如下:
- reducer,将slice中的reducer可以组成一个对象传入此处
- middleware: 可以使用参数,传入其他的中间件
- devTools: 是否配置devTools工具,默认为true;
app.jsx组件
import React, { PureComponent } from 'react'
import Home from './Home'
import { connect } from 'react-redux'
import { ageChange, nameChange } from './store/userState'
export class App extends PureComponent {
render() {
const { name, age } = this.props
return (
<>
<h2>我是APp组件 :{name}-{age}</h2>
<button onClick={e=>this.props.nameChange("测试一下")}>修米关系</button>
<button onClick={e=>this.props.ageChange(123)}>修米关系2</button>
<Home />
</>
)
}
}
const mapStateToProps = (state) => ({
name: state.userState.name,
age: state.userState.age,
})
const mapDispatchToProps = (dispatch) => {
return {
nameChange: (name) => dispatch(nameChange(name)),
ageChange: (num) => dispatch(ageChange(num)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App)
store/index.js
import { configureStore } from '@reduxjs/toolkit'
import userState from './userState'
export default configureStore({
reducer: {userState}
})
store/userState.js
import { createSlice } from '@reduxjs/toolkit'
const userStateSlice = createSlice({
name: "useState",
initialState: {
name: "林夕",
age: 18
},
reducers: {
nameChange(state, action) {
state.name = action.payload
},
ageChange(state, action) {
state.age = action.payload
}
}
})
export const { nameChange, ageChange } = userStateSlice.actions
export default userStateSlice.reducer
1.3.createAsyncThunk :Redux Toolkit的异步操作
- 当createAsyncThunk创建出来的action被dispatch时,会存在三种状态
- pending: action被发出,但是还没有最终的结果
- fulfilled: 获取到最终的结果 (有返回值的结果)
- rejected: 执行过程中有错误或者抛出了异常
createSlice
还接收一个叫 extraReducers
的选项,可以让同一个 slice reducer 监听其他 action types。这个字段应该是一个带有 builder
参数的回调函数,我们可以调用 builder.addCase(actionCreator, caseReducer)
来监听其他 actions
用法一:官方推荐
用法二
二、手写react-redux中的 connect()函数
import { PureComponent } from "react";
import store from '../store'
export default function customConnet(mapStateToProps, mapDispatchToProps) {
return function (WrappedComponent) {
class NewCom extends PureComponent {
constructor(props) {
super(props)
this.state = mapStateToProps(store.getState())
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState(mapStateToProps(store.getState()))
// this.forceUpdate()
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
const state = mapStateToProps(store.getState())
const dispatch = mapDispatchToProps(store.dispatch)
return <WrappedComponent {...this.props} {...state}{...dispatch} />
}
}
return NewCom
}
}
页面组件
import React, { PureComponent } from 'react'
import customConnet from './HOC/customConnet'
import { nameChange } from './store/userState'
export class Custom extends PureComponent {
render() {
const { name } = this.props
return (
<div>
{name}
<button onClick={e=>this.props.nameChange("我是自定义组件高阶")}>我是自定义高阶函数修改</button>
</div>
)
}
}
const mapStateToProps = (state) => ({
name: state.userState.name,
})
const mapDispatchToProps = (dispatch) => {
return {
nameChange: (name) => dispatch(nameChange(name)),
}
}
export default customConnet(mapStateToProps, mapDispatchToProps)(Custom)
对于导入的import store from '../store'
文件耦合性太高我们可以进行一些优化使用前文的context上下文
来传递store路径的解耦
2.1 HOC/hocContext.js
import { createContext } from "react";
export const HOCContext = createContext()
2.2 HOC/customConnet.js
import { PureComponent } from "react";
import { HOCContext } from "./hocContext";
export function customConnet(mapStateToProps, mapDispatchToProps) {
return function (WrappedComponent) {
class NewCom extends PureComponent {
constructor(props, context) {
super(props)
this.state = mapStateToProps(context.getState())
}
componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.setState(mapStateToProps(this.context.getState()))
// this.forceUpdate()
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
const state = mapStateToProps(this.context.getState())
const dispatch = mapDispatchToProps(this.context.dispatch)
return <WrappedComponent {...this.props} {...state}{...dispatch} />
}
}
NewCom.contextType = HOCContext
return NewCom
}
}
2.3 HOC/index.js
export { customConnet } from "./customConnet"
export { HOCContext } from "./hocContext"
2.4 src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store'
import { HOCContext } from "./HOC";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HOCContext.Provider value={store}>
<App />
</HOCContext.Provider>
</React.StrictMode>
);
三、Redux Toolkit的数据不可变性★
- 无论是类组件中的state,还是redux中管理的state;
- 在整个JavaScript编码过程中,数据的不可变性都是非常重要的;
- 前面的章节示例中使用了很多的类似浅拷贝的数据操作,但是尽管如此也可能会出现问题
- 比如过大的对象,进行浅拷贝也会造成性能的浪费;
- 比如浅拷贝后的对象,在深层改变时,依然会对之前的对象产生影响;
- 事实上Redux Toolkit底层使用了immerjs和immutable的一个库来保证数据的不可变性
- 为了节约内存,又出现了一个新的算法: Persistent Data Structure(持久化数据结构或一致性数据结构);
- 用一种数据结构来保存数据
- 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费
四、react中state管理
- 方式一:组件中自己的state管理
- 方式二:Context数据的共享状态
- 方式三:Redux管理应用状态 建议使用
- UI相关的组件内部可以维护的状态,在组件内部自己来维护:
- 大部分需要共享的状态,都交给redux来管理和维护:
- 从服务器请求的数据(包括请求的操作),交给redux来维护: