什么时候用呢?
就像眼镜一样,你该用到的时候你就会知道
- 小型项目当中没有涉及特别复杂的数据传递,但是到了一些大型项目,不同组件之间的传递的数据越来越多,可能使用redux就能减少这些状态管理的负担
redux
- 是react生态当中的状态管理
官网地址
两种模式
- react-redux 传统方式
- RTK 便捷,开箱即用
react-redux 传统方式
- 依赖安装
npm i react-redux redux
创建一个store对象
store/index.js
import { createStore } from 'redux';
// 1. 创建store
// createStore(reduce函数(上一次返回的值,触发的时候传递的参数))
// initState设置一个初始值
const initState = {
count: 0
}
const store = createStore((state=initState, action)=>{
if(action.type === 'increCt'){
state.count = state.count + action.payload
}
// 通过返回值来更新store中的值
return {...state }
});
export default store
store对象的供应
使用 react-redux 当中的 Provider组件来进行
index.js
import store from './store'; // 引入一个 redux store对象 传统方式
import { Provider } from 'react-redux'; // 引入一个Provider组件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
)
获取组件当中使用 redux全局共享的状态
函数组件 通过useSelector 获取redux共享的状态
import { useSelector } from 'react-redux';
// redux的使用 useSelector 来获取redux全局共享的状态
const store = useSelector( state=>{
// state就是redux中的共享的数据
//返回一个新的state 我们实现的对状态的修改,又不想去改变整体,拓展运算符是最优选
return {...state}
})
类组件 通过connect 高阶组件 获取redux共享的状态
//让类组件可以通过this.props来访问redux共享的全局状态
import { connect } from "react-redux";
class Uc extends Component {
componentDidMount(){
console.log('uc props',this.props)
}
increCtInRedux = ()=>{
// 使用redux的dispatch方法来派发action()
// 使用 dispatch来让 createStroe中的reduce函数执行 并传递给到 reduce函数的第二个参数
// 约定传递的这个参数 我们称为 Actions 对象
// Actions 对象中必须有一个 type 属性
//createor(20)可以提取出去
this.props.dispatch(createor(20))
}
render() {
return (
<>
<div>我是用户中心</div>
<div> props in the uc : { JSON.stringify(this.props) }</div>
<button onClick={ this.increCtInRedux }> 测试 dispatch </button>
</>
)
}
}
const mapStateToProps = (state)=>{
// state 就是redux中的共享的数据
// 返回的是会传递给 类组件的this.props
return { ...state }
}
export default connect( mapStateToProps )(Uc)
修改状态-就是为了获取dispatch派发函数
类组件 connect
- 类组件通过 connect之后就可以通过 this.props来使用 dispatch。 通过dispatch来分发一个action对象,dispatch之后会让 store里面的reducer函数执行,然后根据传递的action来对状态进行修改
函数组件 useDispatch
- 函数组件通过引入 useDispatch 这个hook来获得dispatch方法。 然后通过dispatch分发对象 来修改全局共享状态。
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();
const increCtInFC =()=>{
// dispatch({type:'increCt',payload:1})
dispatch(createor(5))
}
两种组件状态的修改
- 就是通过dispatch()进行派发,派发的是一个acticon 对象,这个action对象必须包含一个属性({type:'xxx'})
关于Action Creator
- dispatch分发的是 action对象,但是实际工作当中我们一般不会直接书写一个 acion对象而是通过通过一个函数来返回一个 acion对象。这类的函数我们称为 acion creator
export const createor = (payload)=>{
return {type:'increCt', payload}
}
关于 combineReducer
- 实际开发过程当中,我们并不会直接在createStore当中单独的使用一个函数来处理所有的业务流程,这样会造成代码的冗余,现在更流行的模块化的开发方式,所以 redux 提供了一个 combineReducer 允许我们把多个reducer函数进行合并。
store/reducers/order.js
- 这是订单模块
const orderInitState = {
carts:[],
total:1000
}
const OrderReducer = (state = orderInitState, action) => {
console.log('OrderReducer 执行',action)
return {...state}
};
export default OrderReducer;
store/reducers/user.js
- 这是用户模块
const initState = {
name:'dixon',
money:1
}
const userReducer = (state=initState,action)=>{
console.log('userReducer 执行',action)
return {...state}
}
export default userReducer
模块的合并
store/index.js
import { createStore ,combineReducers} from 'redux';
// 导入2个 reducer 函数
import userReducer from './reducers/user';
import orderReducer from './reducers/order';
// combineReducer 用来合并多个reducer
// combine 合并
const rootReducer = combineReducers({
user:userReducer,
order:orderReducer
})
const store = createStore(rootReducer)
export default store
- 使用了combineReducer dispatch分发action 会让所有的合并的reducer都会触发,并且会得到同样的action。所以需要按照你的需求去合适恰当的修改 state 但是我们可以通过人为的规则约定 我们在 dispatch "模块名/方法名"的方式来 分发action,以便在开发过程当中提升代码的可读性。
- 函数虽然都会被执行,但是里面也没有复杂的计算,性能方面不算太差
const changeOrderTotal = ()=>{
// 一般来说 如果有多个模块的redux 我们触发的type
// 会写成下面的样式 "模块名/方法名"
dispatch({type:'order/setTotal',payload:9999})
}
const orderInitState = {
carts:[],
total:1000
}
const OrderReducer = (state = orderInitState, action) => {
console.log('OrderReducer 执行',action)
const { type, payload } = action;
//根据派发的类型进行相应的修改
if(type==='order/setTotal'){
state.total = payload
}
return {...state}
};
export default OrderReducer;
RTK 模式
- 依赖安装
npm install @reduxjs/toolkit
使用configureStore 创建store对象
store/index.js
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./slices/user";
import orderReducer from "./slices/order";
const store = configureStore({
reducer: {
user: userReducer,
order: orderReducer
}
})
export default store
关于slice
- slice对象,RTK当中提供了 createSlice 方法让我们快捷的创建reducer函数并且附带的创建了触发这些函数对应的 action creator
import { createSlice } from "@reduxjs/toolkit";
const userSlice = createSlice({
name: "user", // 模块名称 命名空间
initialState: { // 初始状态
name: 'dixon',
age: 19
},
reducers: { // 修改状态的方法
setName(state,action){
// state当前的模块的下state
state.name = action.payload
},
setAge(state,action){
console.log('修改年龄')
state.age = state.age - action.payload
}
}
})
// createSlice 返回一个slice实例
// slice实例中有两个属性
// 第一个 actons 是 action creator 用来触发修改状态的方法
// console.log('userSlice creator',userSlice.actions)
// console.log('userSlice.actions',userSlice.actions.setName(9999))
export const { setName, setAge } = userSlice.actions
// 第二个 reducer 是一个reducer函数
export default userSlice.reducer
store对象的供应
- 使用 react-redux 当中的 Provider组件来进行
import store from './store/rtk'; // 引入一个 redux store对象 传统方式
import { Provider } from 'react-redux'; // 引入一个Provider组件
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}> <RouterProvider router={router} /> </Provider>
)
获取共享状态
- 组件当中获取redux 共享状态的方法跟传统方式一致
类组件 connect
- 类组件通过 connect之后就可以通过 this.props来使用 dispatch。 通过dispatch来分发一个action对象,dispatch之后会让 store里面的reducer函数执行,然后根据传递的action来对状态进行修改
函数组件 useDispatch
- 函数组件通过引入 useDispatch 这个hook来获得dispatch方法。 然后通过dispatch分发对象 来修改全局共享状态。
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();
const increCtInFC =()=>{
// dispatch({type:'increCt',payload:1})
dispatch(createor(5))
}
修改状态
- 也是使用 dispatch来分发action来实现触发reducer函数来修改状态,但是因为使用了createSlice所以slice对象会给我们提供了对应的 action Creator对应的 actionCretor进行使用即可
slices/users.js
export const { setName, setAge } = userSlice.actions
cp.js
import { setName,setAge } from '../store/slices/user';
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();
const Home = () => {
const beYoung = ()=>{
dispatch(setAge(1))
}
return (
<div>
<button onClick={beYoung}> 梦想成真 </button>
</div>
)
}
关于异步操作
类组件实现方法
使用connect 传入 mapDIspatchToProps 来实现
- 对于类组件而言我们是可以使用 connect高阶组建成 传入 第二个参数 来实现在组件外部构建一些异步操作的函数,通过connect挂载到 类组件的 this.props
- 注意事项:如果在connect传入了 第二个参数 mapDIspatchToProps ,dispatch将不再挂载在 this.props上(也就是组件内部得不到distatch)
- 这种方法可以使得我们抽离逻辑,实现模块化的开发
函数组件实现方法
使用中间件 redux-thunk
- 安装 redux-thunk
npm i redux-thunk
-
配置 在store/index.js
-
配置后之之后 我们的dispatch 不仅仅能传入一个action对象也可以thunk中间件的主要用途就是允许我们在 dispatch的传入一个函数而不一定要是一个action对象,而这个传入的函数将会得到一个dispatch的参数 方便其在内部完成了异步操作之后 分发action对象
- 这种方法可以使得我们抽离逻辑,实现模块化的开发
数据持久化
- redux-persist 可以帮我们快捷的实现redux共享数据的持久化,避免刷新页面之后导致共享的数据重置的麻烦。
- 安装 redux-persist
npm i redux-persist
- 引入对应的持久化类库
// 引入对应的持久化的类库
import { persistStore,persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // localStorage
import storageSession from 'redux-persist/lib/storage/session'; // sessionStorage
- 配置存储配置的信息模板
const persistConfig = {
key:'root',// 在浏览器的本地存储当中 使用哪个key来保存redux 的全局状态的数据
storage: storage , // 使用localStorage还是sessionStorage来保存数据 上面的storage和storageSession二选1
}
- 多个module 使用combineReducers()时
- 使用persistReducer(配置信息, reducer)单独的给每个reducer函数进行持久化处理
- 在原有的reduer函数当中使用 persistReducer来进行处理
const rootReducer = combineReducers({
user: persistReducer({ key:'user' ,storage} ,userReducer),
// 使用persistReducer(配置信息, reducer)单独的给每个reducer函数进行持久化处理
order: persistReducer({key:'order', storage} ,orderReducer)
})
const store = createStore(rootReducer,applyMiddleware(thunk))
- 单独的导出一个持久化对象
export const persistor = persistStore(store) // 还要单独的生成一个持久化对象
export default store
- 使用持久化对象
import { PersistGate } from 'redux-persist/integration/react'
import store ,{persistor} from './store'
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
原来的代码
</PersistGate>
</Provider>
);
};