文章目錄
- 一.Redux的基礎用法
- 1.示例:普通網頁中的Redux計步器
- 2.Redux管理數據的流程
- 3.配套工具和環境準備
- 3.1.配套工具
- 3.2.環境準備
- 4.示例:React項目中的Redux計步器
- 思路
- 步驟
- step1:創建子模塊
- step2:導入子模塊
- step3:注入store實例
- step4:React組件內使用store中的數據
- step5:在組件內修改store中的數據
- step6:優化---定義代參actionCreater來傳參
- 5.示例:使用Redux管理異步狀態操作
- 5.1.創建子模塊
- 5.2.導入子模塊
- 5.3.注入store實例
- 5.4.在組件中使用
- 二.Redux案例:某團外賣
- 1.開發前準備
- 2.渲染商品分類和商品列表
- 2.1.創建子模塊takeaway
- 2.2.引入子模塊
- 2.3.把store注入到組件中
- 2.4.在根組件App中使用和渲染數據
- 3.點擊分類實現高亮
- 3.1.RTK管理新狀態activeIndex
- 3.2.組件中:點擊觸發action更新activeIndex,動態控制激活類名
- 4.點擊分類項,實現商品列表的切換顯示
一.Redux的基礎用法
Redux之于React,類似于Vuex或Pinia之于Vue
Redux可以獨立于框架運行,作用是通過幾種管理的方式管理應用的狀態
1.示例:普通網頁中的Redux計步器
由于Redux可以獨立于React框架,因此可在網頁中使用
步驟:
-step1:定義一個reducer函數
該函數根據當前想要做的修改返回一個新狀態,新狀態的作用是根據不同的Action對象,返回不同的新state(原則:狀態不可變)
示例:function myReducer(state,action){}-step2:使用createStore方法中的reducer函數生成一個store實例對象
示例:const store=Redux.createStore(myReducer)-step3:使用store.subscribe方法,訂閱數據的變化
(數據一旦變化就會得到通知)
示例:storesubscribe(()=>{})-step4:使用store.dispatch方法,提交action對象,觸發數據變化
dispatch提交一個action來更新狀態
示例:store.dispatch({type:'INCREMENT'})-step5:使用store.getState方法,獲取最新的狀態并更新到視圖中
示例:const state=stor.getState()
完整代碼:
<!DOCTYPE html>
<html>
<head><title>Redux計數器示例</title><!-- 引入Redux庫 --><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.1/redux.min.js"></script>
</head>
<body><h1>Redux計數器</h1><div><button id="decrement">-</button><span id="counter-value">0</span><button id="increment">+</button></div><script>// 1.定義reducer函數function myReducer(state = { count: 0 }, action) {// 判斷type:type的值是從action對象中獲取的,而action對象是由dispatch函數生成的。if (action.type === 'INCREMENT') return { count: state.count + 1 };//增加:返回新的狀態對象if (action.type === 'DECREMENT') return { count: state.count - 1 };}// 2.使用reducer函數生成store實例const store = Redux.createStore(myReducer);// 3.通過store實例的subscribe訂閱數據變化store.subscribe(() => {//該回調會在每次store更新時被調用。// 5.通過store實例的getState方法獲取當前狀態值,并更新到頁面上。const state = store.getState();// 更新頁面上的計數器顯示document.getElementById('counter-value').textContent = state.count;});// 4.通過store實例的dispatch函數提交action更改狀態/*在Redux中修改數據的唯一方式是:通過dispatch提交一個action對象來更新狀態*/document.getElementById('increment').addEventListener('click', () => {store.dispatch({ type: 'INCREMENT' });// 提交增加計數器的action對象});document.getElementById('decrement').addEventListener('click', () => {store.dispatch({ type: 'DECREMENT' });// 提交減少計數器的action對象});</script>
</body>
</html>
2.Redux管理數據的流程
出于職責清晰和數據流向明確的考量,在Redux中,把修改數據的流程分為三個核心概念:
state
對象:存放管理的數據狀態action
對象:描述如何修改數據的reducer
函數:形參是state和action
,根據acton
對象的描述生成一個新的state
3.配套工具和環境準備
3.1.配套工具
Redux Toolkit
插件(RTK)
官方推薦的編寫Redux邏輯的方式,是一套工具的集合,能簡化書寫方式
優點:簡化store的配置方式內置immer支持可變式狀態修改內置thunk更好的異步創建
react-dedux
插件
用來鏈接Redux和React組件的中間件
(Redux向React組件獲取狀態,React組件向Redux更新狀態,都可以通過這個中間件)
3.2.環境準備
創建項目:npx create-react-app my-react-redux(項目名)
進入項目目錄:cd my-react-redux
安裝配套工具:npm i @reduxjs/toolkit react-redux(插件)
啟動項目:npm run start
項目store目錄:
src
├─store 用于集中狀態管理
│ ├─modules 用于在內部編寫業務分類的子store(應用通常有多個子store模塊)
│ │ └─index.js 作用是組合modules中的所有子模塊并在store中導入
4.示例:React項目中的Redux計步器
思路
- Redux store配置:配置
counterStore
模塊,配置根store
并導入counterStore
模塊 - React組件:注入
store(react-redux)
,使用和修改store
中的數據
步驟
step1:創建子模塊
使用React Toolkit
的createSlice()方法
創建counterStore
子模塊
//store/modules/counterStore.jsimport {createSlice} from "@reduxjs/toolkit";
const counterstore=createSlice({name:"counter",// 初始化stateinitialState:{//----類似于Vuex中的Storecount:0},// 定義reducer函數reducers:{//----類似于vuex中的mutationsincrement(state){state.count++},decrement(state){state.count--}}
})// 解構出actionCreater函數
const {increment,decrement}=counterstore.actions
// 獲取reducer函數
const reducer=counterstore.reducer
// 按需導出actionCreater函數
export {increment,decrement}
// 默認導出reducer函數
export default reducer
step2:導入子模塊
//src/store/index.jsimport { configureStore } from "@reduxjs/toolkit";
// 導入子模塊的reducer
import counterReducer from "./modules/counterStore";
export const store = configureStore({reducer: {// 注冊子模塊的reducer函數counter: counterReducer,},
});
step3:注入store實例
react-redux
可以鏈接Redux和React,
其內置的Provider
組件可以通過store參數,把創建好的store實例注入到應用中,從而正式建立鏈接
//src/index.jsimport { Provider } from 'react-redux';
import { store } from './store';
....{/* 注入:將store注入到Provider組件的props中,然后包裹App組件。 */}<Provider store={store}><App /></Provider>
step4:React組件內使用store中的數據
鉤子函數useSelector可以把store中的數據映射到組件中
//App.jsimport { useSelector } from "react-redux";
function App() {const { count } = useSelector((state) => state.counter);//來自src/store/index.js的reducer函數return (<div className="App"><h1>Counter: {count}</h1></div>);
}
export default App;
step5:在組件內修改store中的數據
鉤子函數useDispatch可以生成dispatch函數,用于提交action對象
//App.js
// 導入actionCreater
import {increment,decrement} from "./store/modules/counterStore";{/* 增加按鈕,觸發 increment action */}
<button onClick={() => dispatch(increment())}>+</button>
{/* 顯示計數器 */}
<h1>Counter: {count}</h1>
{/* 減少按鈕,觸發 decrement action */}
<button onClick={() => dispatch(decrement())}>-</button>
*子模塊counterStore中,在reducers中定義的increment()
和decrement()
被稱為actionCreater方法
step6:優化—定義代參actionCreater來傳參
App組件新增"addTo10"和"addTo20"兩個按鈕,
步驟:
* 在reducers的同步修改方法中添加action對象參數
* 在調用actionCreater時傳參
* 參數傳到action對象的payload屬性上
代碼:
//counterStore.js
.....// 定義reducer函數reducers:{...addToNum(state,action){state.count+=action.payload}}
// 解構出actionCreater函數
const {increment,decrement,addToNum}=counterstore.actions
// 獲取reducer函數
const reducer=counterstore.reducer
// 按需導出actionCreater函數
export {increment,decrement,addToNum}
// 默認導出reducer函數
export default reducer//App.js
import {increment,decrement,addToNum} from "./store/modules/counterStore";<button onClick={() => dispatch(addToNum(10))}>+10</button><button onClick={() => dispatch(addToNum(20))}>+20</button>
總結:
useSelector
:獲取store中的數據useDispatch
:獲取dispatch方法- 得到要提交的action對象:
dispatch(increment())//執行store模塊中導出的actionCreater方法,即increment()方法
5.示例:使用Redux管理異步狀態操作
5.1.創建子模塊
//新建store/modules/channelStore.js//step1:創建子模塊channelStore.js,代碼不變
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";const channelstore=createSlice({name:'channel',initialState:{channelList:[]},reducers:{setChannel(state,action){state.channelList=action.payload}}
})/**異步請求部分 */
// step2:單獨封裝一個函數
const fetchChannelList=()=>{// const dispatch=useDispatch()// 在函數內部return一個新函數,// 在新函數中封裝異步請求,// 并調用actionCreater方法傳入異步數據生成一個action對象并使用dispatch方法提交return async (dispatch)=>{const res=await axios.get("https://geek.itheima.net/v1_0/channels")dispatch(setChannel(res.data.data.channels))console.log("res:",res.data.data.channels)}
}// 解構出actionCreater函數
const {setChannel}=channelstore.actions
// 按需導出actionCreater函數和異步請求函數
export {setChannel,fetchChannelList}// 獲取reducer函數
const reducers=channelstore.reducer
// 默認導出reducer函數
export default reducers
5.2.導入子模塊
//store/index.js
import { configureStore } from "@reduxjs/toolkit";
// 導入子模塊的reducer
import counterReducer from "./modules/counterStore";
import channelReducer from "./modules/channelStore";
export const store = configureStore({reducer: {// 注冊子模塊的reudcer函數counter: counterReducer,channel: channelReducer,},
});
5.3.注入store實例
略,無新增代碼
5.4.在組件中使用
//App.js
import { fetchChannelList } from "./store/modules/channelStore";
import { useEffect } from "react";function App(){// 使用 dispatch 來觸發 actionconst dispatch = useDispatch();// 使用useEffect來觸發異步請求執行useEffect(() => {// 觸發 fetchChannelList actiondispatch(fetchChannelList());}, [dispatch]);return(<ul>{channelList.map((item) => (<li key={item.id}>{item.name}</li>))}</ul>)
}
二.Redux案例:某團外賣
功能:
* 1.商品列表和分類渲染* 2.添加商品* 3.購物車操作* 4.訂單數量統計和高亮實現
思路:
???? 使用Redux Toolkit(RTK)
來管理應用狀態,
?????組件負責數據渲染和dispatch action
1.開發前準備
step1:從遠程倉庫克隆本地git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git
step2:安裝所有依賴npm i
step3:啟動mock服務(內置json-server)npm run serve
step4:啟動前端服務npm run start
2.渲染商品分類和商品列表
2.1.創建子模塊takeaway
//store/modules/takeaway.js
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";const foodsStore = createSlice({name: "foods",initialState: { foodsList: [] },reducers: {setFoodsList(state, action) {state.foodsList = action.payload;}}
})// 解構createAction方法
const { setFoodsList } = foodsStore.actions// 異步
const fetchFoodsList = () => {return async (dispatch) => {// 獲取數據const res = await axios.get("http://localhost:3004/takeaway");// dispatch提交,生成新的action對象dispatch(setFoodsList(res.data))}
}// 按需導出
export { setFoodsList, fetchFoodsList }// 獲取reducer
const reducer = foodsStore.reducer
// 默認導出
export default reducer
2.2.引入子模塊
//src/store/index.js import { configureStore } from "@reduxjs/toolkit";
// 導入子模塊的reducer
import foodsReducer from "./modules/takeaway";export default configureStore({reducer: {// 這里的key要和子模塊的name一致foods: foodsReducer,},
});
2.3.把store注入到組件中
//index.jsimport { createRoot } from 'react-dom/client'
import {Provider} from 'react-redux'
import App from './App'
import store from './store'const root = createRoot(document.getElementById('root'))
root.render(// 注入:把store注入到Provider組件中,然后包裹App組件<Provider store={store}><App /></Provider>
)
2.4.在根組件App中使用和渲染數據
//App.js
import { useDispatch,useSelector } from 'react-redux'
import { useEffect } from 'react'
import { fetchFoodsList } from './store/modules/takeaway'//注釋掉靜態數據const foodsList=[]const App=()=>{/*觸發action執行*/const dispatch=useDispatch()// 使用useEffect鉤子,在組件加載完成后獲取外賣商品列表useEffect(()=>{dispatch(fetchFoodsList())// 派發一個異步獲取外賣商品列表的action},[dispatch])/*獲取foodsList渲染數據列表*/// 使用useSelector鉤子獲取foodsList數據const {foodsList} = useSelector(state => state.foods)
}
3.點擊分類實現高亮
即點擊左邊側邊欄中的分類獲得高亮選中,右邊的對應內容在頁面中渲染
高亮和對應的內容如何綁定?
- 記錄當前的點擊項
activeIndex
- 動態控制類名,判斷條件
activeIndex===index
步驟:
????1.RTK管理新狀態activeIndex
????2.組件中點擊觸發action,更改activeIndex
????3.動態控制激活類名顯示
3.1.RTK管理新狀態activeIndex
//store/modules/takeaway.jsconst foodsStore = createSlice({name: "foods",initialState: {foodsList: [] ,//菜單列表activeIndex: 0 // 當前激活的菜單索引},reducers: {.....// 更改當前激活的菜單索引changeActiveIndex(state, action) {state.activeIndex = action.payload;}}
})
// 解構createAction方法
const { setFoodsList ,changeActiveIndex} = foodsStore.actions
// 按需導出
export { fetchFoodsList, changeActiveIndex }
3.2.組件中:點擊觸發action更新activeIndex,動態控制激活類名
//src/components/menus/index.jsimport classNames from 'classnames'
import './index.scss'
import { useSelector, useDispatch } from 'react-redux'
import { useEffect } from 'react'
import { changeActiveIndex } from '../../store/modules/takeaway'const Menu = () => {// 從redux中獲取數據
const { foodsList,activeIndex} = useSelector((state) => state.foods);
////過濾條件: 過濾出所有標簽,并且去除重復的
const menus=foodsList.map(item=>({tag:item.tag,name:item.name}))
const dispatch = useDispatch()// 使用useEffect觸發actionuseEffect(() => {dispatch(changeActiveIndex(0))//初始值讓渲染有默認選中}, [dispatch])return (<nav className="list-menu">{/* 添加active類名會變成激活狀態 */}{menus.map((item, index) => {return (<divonClick={() => {dispatch(changeActiveIndex(index))}}key={item.tag}className={classNames('list-menu-item',activeIndex===index&&'active')}>{item.name}</div>)})}</nav>)
}
export default Menu
4.點擊分類項,實現商品列表的切換顯示
條件渲染,控制對應項的顯隱activeIndex===index&&<div></div>
//App.js
const { foodsList, activeIndex } = useSelector(state => state.foods)
....
{/* 外賣商品列表 */}
{foodsList.map((item, index) => {return (activeIndex === index && <FoodsCategorykey={item.tag}// 列表標題name={item.name}// 列表商品foods={item.foods}/>)
})}