一、独立使用Redux
1. 关于Redux
(1)Redux是React最常用的集中状态管理工具,可独立于React框架运行。
2. 使用步骤
(1)定义一个 。(根据当前想要做的修改返回一个 新的状态)
(2)使用 createStore 方法 并传入 reducer函数,生成一个 。
(3)使用 store实例对象的 订阅数据的变化。(数据变化时会触发此方法)
(4)使用 store实例对象的 传入
,触发数据变化。(通过action对象告诉reducer你想调用哪个方法去修改数据)
(5)使用 store实例对象的 获取最新的状态数据。
3. Redux把整个数据修改流程分为三个核心概念
(1)state:一个对象,存放着公共数据状态。
(2)action:一个对象,用于描述想要怎么修改数据。
(3)reducer:一个函数,根据action的描述生成一个新的state。修改state数据的方法都在这里。
function reducer(state={},action){
if(action.type==='1'){}
if(action.type==='2){}
}
- reducer函数有两个参数,第一个是 state数据,可以赋默认值;第二个是 action对象,可以根据type属性来区分不同的操作。
- Redux中修改数据的唯一方法就是 提交一个action对象。
- 在reducer函数中修改state数据时,必须用 新的数据 替换旧的state数据,比如新的对象、新的数组。
<body>
<button onclick="handleASC()">+</button>
<button onclick="handleDESC()">-</button>
<script src="https://unpkg.com/redux@4.2.1/dist/redux.js"></script>
<script>
// 1.定义reducer函数,根据传入的action对象,返回不同的新的state数据
// 参数state: 管理的数据的初始状态
// 参数action对象:根据type属性确定想要哪种操作
function reducer(state = { count: 0 }, action) {
if (action.type === 'ASC') {
return { count: state.count + 1 } // 返回新的state数据
} else if (action.type === 'DESC') {
return { count: state.count - 1 }
} else {
return state
}
}
// 2.使用createStore方法,传入reducer函数,生成store实例
const store = Redux.createStore(reducer)
// 3.使用store实例对象的subscribe方法订阅数据变化
store.subscribe(() => {
// 5.根据store实例对象的getState方法获取到最新的state数据
console.log(store.getState())
})
// 4.使用store实例对象的dispatch方法,传入action对象来更新state数据
const handleASC = () => {
store.dispatch({ type: 'ASC' })
}
const handleDESC = () => {
store.dispatch({ type: 'DESC' })
}
</script>
</body>
二、在React项目中使用Redux:准备工作
1. 安装必要插件
(1)React Toolkit:官方推荐编写Redux逻辑的方式,是一套工具的集合,可以 简化书写方式。
(2)react-redux:连接 Redux 和 React组件 的中间件。
2. 创建目录
(1)在src目录下创建一个名为 store 的文件夹,包含 modules文件夹 和 index.js 文件。
(2)index.js文件的作用是整合modules中的模块化文件,并导出 根store 实例对象。
三、在React项目中使用Redux:模块化开发
1. Redux子模块文件配置
(1)通过 createSlice方法 创建一个 子模块的 store实例对象。
(2)配置项 name 是 模块名称,配置项 initialState 对象用来存放 初始化的state数据,
(3)配置项 reducers 对象存放修改数据的 。
① 同步方法的第一个参数是state,可以 访问state数据。
② 同步方法的第二个参数是action,通过 action.payload 属性可以 获取自定义参数。
(4)通过store实例对象的 actions 属性,获取 ,
和reducers中修改数据状态的方法同名,并按需导出。
(5)通过store实例对象的 reducer 属性获取 子模块的reducer,并默认导出。
import { createSlice } from '@reduxjs/toolkit'
// 1.通过createSlice方法创建一个store子模块实例对象
const countStore = createSlice({
// 2.1模块名称
name: 'counter',
// 2.2初始化state数据
initialState: {
value: 100,
},
// 2.3修改state数据状态的方法,同步方法,可以直接修改数据
reducers: {
// 不传参
increment: (state) => {
state.value = state.value + 1
},
// 传参
addNum: (state, action) => {
state.value = state.value + action.payload
}
}
})
// 3.获取actionCreater方法,调用这个方法可以生成action,React组件中调用dispatch方法时,传入的参数就是action对象
const { increment, addNum} = countStore.actions
// 4.获取reducer
const counterReducer = countStore.reducer
export { increment, addNum }
export default counterReducer
2. Redux入口文件配置
(1)在store入口文件index.js中,通过 configureStore方法 创建 根store实例对象。
(2)在 reducer配置对象 中配置子模块。
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './modules/counterStore.js' // 导入子模块的reducer
// 1.通过configureStore创建 根store
const store = configureStore({
// 2配置子模块的reducer
reducer: {
counter: counterReducer // 子模块名为counter
}
})
export default store;
3. 全局挂载Redux
(1)在项目入口文件index.js中导入 根store实例 和 Provider。
(2)用Provider包裹 项目根组件 <App/> 或者是路由组件 <RouterProvider></RouterProvider>,将store传入。
import store from './store' // 1.导入根store实例对象
import { Provider } from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
4. 在React组件中使用Redux数据和方法
(1)通过 useSelector 获取模块化的 store数据。
const res = useSelector((state) => state.子模块名称)
(2)通过 useDispatch 生成 dispatch。
const dispatch = useDispatch()
(3)用dispatch调用 修改store数据的方法,入参是一个 action对象。
dispatch(生成action对象的方法())
import { useSelector, useDispatch } from "react-redux";
import {increment, addNum} from './store/modules/counterStore' // 1.导入生成action对象的方法
function App() {
const res = useSelector((state) => state.counter) // 2.获取store数据
const dispatch = useDispatch() // 3.生成dispatch
return (
<div className="App">
<span>{ res.value }</span>
<button onClick={() => dispatch(increment())}>点击自增1</button>
<button onClick={()=> dispatch(addNum(20))}>点击自增20</button>
</div>
);
}
export default App;
5. 异步操作
(1)如果要进行异步操作,则在前面四步的基础上做进一步处理。
(2)reducers中的同步方法可以 直接修改 state数据;异步操作要调用reducers的同步方法,间接修改 state数据。
(3)在store子模块中,单独封装一个函数,函数内部 return 一个新函数,在新函数中:
① 封装异步请求获取数据。
② 用dispatch调用同步方法并传递数据,dispatch从 新函数的形参 获取。
(4)React组件中的调用方式不变。
import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios'
const channelStore = createSlice({
name: 'channel',
initialState: {
value: []
},
reducers: {
setValue(state, action) {
state.value = action.payload
}
}
})
const { setValue } = channelStore.actions
const channelReducer = channelStore.reducer
// 封装异步操作
const getChannelList = () => {
return async (dispatch) => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels') // 1.异步操作
dispatch(setValue(res.data.data.channels)) // 2.调用reducers的同步方法,并传递参数
}
}
export { getChannelList }
export default channelReducer
四、ZuStand的基本使用
1. 关于ZuStand
(1)ZuStand是一个全局状态管理工具,拥有基于 hooks 的舒适的API。
(2)安装:npm install zustand
2. 项目中使用
(1)在store入口文件中,使用 create 函数创建 useStore函数,接收一个回调函数作为参数。
(2)回调函数接收一个 set 参数;回调函数返回一个对象,存放 state数据 和 修改state数据的方法。
(3)修改数据时,必须在方法内部调用 set 函数进行修改,通过 state 参数访问state数据。
const 实例对象名 = create((set)=>{return {
数据名:数据值,
方法名:(自定义参数)=>set((state)=>({数据名:state.数据名 + 1}))
}})
import { create } from 'zustand'
const useStore = create((set) => {
return {
// 1.初始化的数据
number: 0,
// 2.修改数据的方法。newNumber为参数; state可以访问到state数据; 修改数据必须调用set方法
setNumber1: (newNumber) => set((state) => ({ number: state.number + newNumber })),
// 3.异步操作直接写到方法内部即可
setNumber2: (newNumber) => {
setTimeout(() => {
set(state=>({number: state.number - newNumber}))
},1000)
}
}
})
export default useStore
(4)React组件中使用,引入store模块创建的 useStore函数,从中获取到state数据和修改state数据的方法。
import useStore from './store/index.js'
function fn(){
const {number, setNumber1, setNumber2} = useStore() // 调用useStore函数,获取数据和方法
return <button onClick={()=>setNumber1(100)}>{number}</button>
}
3. 项目中模块化使用
(1)store子模块
const channelStore = (set) => {
return {
// 1.初始化的数据
number: 0,
// 2.修改数据的方法。newNumber为参数; state可以访问到state数据; 修改数据必须调用set方法
setNumber1: (newNumber) => set((state) => ({ number: state.number + newNumber })),
// 3.异步操作直接写到方法内部即可
setNumber2: (newNumber) => {
setTimeout(() => {
set(state=>({number: state.number - newNumber}))
},1000)
}
}
}
export default channelStore
(2)store入口文件
import { create } from 'zustand'
import channelStore from './modules/channel'
const useStore = create((...a) => {
return {
...channelStore(...a),
}
})
export default useStore
(3)React组件中使用方法不变,同上。
import useStore from './store/index.js'
function fn(){
const {number, setNumber1, setNumber2} = useStore() // 调用useStore函数,获取数据和方法
return <button onClick={()=>setNumber1(100)}>{number}</button>
}