React核心价值与前置知识
时刻保持对知识的渴望 家人们 开学!!!
核心价值
组件化(易开发易维护)
数据驱动视图 :定义好数据和ui的显示规则 即
UI=f(state)
- 只关注业务数据修改,不在操作DOM 增加开发效率
使用vite创建Recat项目

开发规范
使用 prettier & eslint 规范开发
- eslint 检查语法语义
- prettier 检查代码风格
#eslint :
npm install eslint@typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
#prettier:
npm install prettier eslint-config-prettier eslint-plugin-prettier --save-dev
vite和 webpack的区别
webpack是一个非常流行的前端打包工具 比较经典 Create-React-App 是使用webpack作为打包工具的
vite 既是构建工具 又是打包工具
vite的特点:
- Vite打包项目 在启动和代码更新时更快
- vite使用了
es Module语法(仅开发环境)
React JSX语法
内容 :
- JSX语法
- 组件和props
- 实战: 列表页
JSX特点:
- JSX是js的扩展 写在js代码里面 组件的ui结构
- 语法和html很相似
- 不只是React独有
标签
- 首字母大小写的区别 , 大写字母是自定义组件
- 标签必须闭合 如
<input>在jsx是非法的 - 每段JSX中只有一个根节点

属性
和html基本相似
- class要改为 className
- style要使用js对象 不能是string 而且key需要使用驼峰写法
如下

在JSX中插入js变量
- 使用
{}可以插入JS变量 函数 表达式 - 可以插入文本 属性
- 可以用于注释
代码案例

条件判断
常见的if else 可以通过{}的方式实现,但是在JSX中代码一多就显得不够实用了 以下三种方法可以解决:
- 使用
&& - 使用三元表达式
- 使用函数来判断
比如这样:反之如果flag等于false 就不会出现hello

效果:

三元运算符:flag为判断条件 来控制标签的显示

效果:

函数:
function isShowHello(){
if (flag)return <p>show hello</p>
return <p>defaultHello</p>
}

效果 :

循环
- 使用map来循环
- 每一个循环项(item)都要有key
- key需要具有唯一性
实现
const list = [
{username:'zhangsan', name:"张三"},
{username:'shuangyue', name:"双月"},
{username:'lisi', name:"李四"},
]
{/*循环*/}
<div>
{list.map(user=>{
const {username,name} = user
return <li key={username}>{name}</li>
})}
</div>
效果:

PS : 不建议使用 index 如 :

因为我们的key 需要具有唯一性
小结实战 列表页
开发一个列表页

调整一下显示的jsx

保证这个代码结构简洁 ,然后就可以开始开发了
import React from 'react';
import './App1.css';
function App() {
const questionList = [
{id: 'q1', title: '问卷1', isPublished: true},
{id: 'q2', title: '问卷2', isPublished: true},
{id: 'q3', title: '问卷3', isPublished: true},
{id: 'q4', title: '问卷4', isPublished: false}
]
function edit(id) {
console.log('edit', id);
}
return (<div>
<h1>列表详情页</h1>
<div>
{questionList.map(question => {
const {id, title, isPublished} = question;
return <div key={id} className="list-item">
<strong>{title}</strong>
{isPublished ? <span style={
{color: "green"}}>已发布</span> : <span>未发布</span>}
<button onClick={() => edit(id)}>编辑问卷</button>
</div>
})}
</div>
</div>)
}
export default App;
css
.list-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 16px;
display: flex;
justify-content: center;
}
效果

组件
react 一切皆是组件
- 组件拥有一个ui片段
- 拥有独立的逻辑和显示
- 可大可小 可以嵌套
组件拆分的价值和意义
- 组件嵌套来组织的 ui 结构 和 html 一样没有学习成本
- 良好的拆分组件利于代码维护和多人协同开发
- 封装公共组件或者直接使用第三方组件复用代码
好的组件化 逻辑是清晰的 更能提升开发效率并且更加的美观易读

我们可以将组件理解成一个一个的函数
使用我们之前的列表页代码 拆分成组件 list1

然后用improt的方式 引入到listdemo中

这样我们的总框架就没有那么多的代码冗余 需要修改对应的代码 只需要寻找对应的组件文件即可
属性 props
- 组件可以嵌套 有层级关系
- 父组件可以向子组件传递数据
- props是只读对象
props 其实就是实现差异化组件信息传递的一种手段
实践
将之前循环内显示数据的div拆出来抽象成组件:QuestCard.tsx 。 CSS还是和之前的内容一样
使用 ts主要是方便传入泛型
QuestCard.tsx
import React, {FC} from "react";
import './QuestCard.css'
type proptype = {
id: string,
title: string,
isPublished: boolean
}
export const QuestCard: FC<proptype> = (props) => {
const {id, title, isPublished} = props;
function edit(id) {
console.log('edit', id);
}
return (
<div key={id} className="list-item">
<strong>{title}</strong>
{isPublished ? <span style={
{color: "green"}}>已发布</span> : <span>未发布</span>}
<button onClick={() => edit(id)}>编辑问卷</button>
</div>)
}

改造list1.jsx 这样就将显示问卷卡片抽取出来为一个独立的组件了
import React from "react";
import './list1.css';
import {QuestCard} from "./QuestCard";
export const List1 = () => {
const questionList = [
{id: 'q1', title: '问卷1', isPublished: true},
{id: 'q2', title: '问卷2', isPublished: true},
{id: 'q3', title: '问卷3', isPublished: true},
{id: 'q4', title: '问卷4', isPublished: false}
]
return (
<div>
<h1>列表详情页</h1>
<div>
{questionList.map(question => {
const {id, title, isPublished} = question;
return <QuestCard key={id} id={id} title={title} isPublished={isPublished}/>
})}
</div>
</div>)
}
小结:
- 如何定义和使用组件
- props-父组件给子组件传递数据
- 重构列表页 抽象出
QuestionCard
效果

children
场景: 当我们把内容签到在子组件标签中时,父组件会自动的在名为 children的prop中接受内容

子组件传递父组件
顾名思义 其实就是子组件给父组件传递信息
function Son({onGetSonMsg}) {
// son 中的数据
const sonMsg = 'this is son msg';
return <div>this is son
<button onClick={() => onGetSonMsg(sonMsg)}>sendMsg</button>
</div>
}
function AppDemo() {
const [msg, setMsg] = useState('')
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg(msg)
}
return <div>
this is APP Son send msg =>{msg}
<Son onGetSonMsg={getMsg}/>
</div>
}
兄弟组件传递
使用状态提升实现兄弟组件通信
- 其实就是有共同父组件的两个子组件传递信息
- a 传递给父组件 然后由父组件 传递给 b

代码
import {useState} from "react";
function A({onGetAName}) {
const name = "a name"
return <div>this is A
<button onClick={() => onGetAName(name)}>send</button>
</div>
}
function B({pushAName}) {
return <div>this is B
{pushAName}
</div>
}
function AppDemo() {
const [aName, setAName] = useState('');
const getAName = (name) => {
console.log(name)
setAName(name)
}
return <div>
this is app
<A onGetAName={getAName}/>
<B pushAName={aName}/>
</div>
}
export default AppDemo;
function A({onGetAName}) {
const name = "a name"
return <div>this is A
<button onClick={() => onGetAName(name)}>send</button>
</div>
}
function B({pushAName}) {
return <div>this is B
{pushAName}
</div>
}
function AppDemo() {
const [aName, setAName] = useState('');
const getAName = (name) => {
console.log(name)
setAName(name)
}
return <div>
this is app
<A onGetAName={getAName}/>
<B pushAName={aName}/>
</div>
}
效果

React 拓展
React.memo
允许组件在Props没有改变的情况下 跳过渲染
react组件默认的渲染机制 : 父组件重新渲染的时候子组件也会重新渲染
import React, {
useState} from 'react';
function Son() {
console.log('子组件被重新渲染了')
return <div>this is son</div>
}
const ReactMemoDemo = () => {
const [, forceUpdate] = useState()
console.log('父组件重新渲染了')
return (
<>
<Son/>
<button onClick={
() => forceUpdate(Math.random())}>update</button>
</>
)
};
export default ReactMemoDemo;

这个时候使用 memo包裹住组件 就可以避免 但是 注意 只考虑props变化才能使用\
import React, {
memo, useState} from 'react';
// function Son() {
// console.log('子组件被重新渲染了')
// return <div>this is son</div>
// }
const MemoSon = memo(function Son() {
console.log("我是子组件 我被渲染了")
return <div>this is son</div>
})
const ReactMemoDemo = () => {
const [, forceUpdate] = useState()
console.log('父组件重新渲染了')
return (
<>
<MemoSon/>
<button onClick={
() => forceUpdate(Math.random())}>update</button>
</>
)
};
export default ReactMemoDemo;

React.memo 比较机制
React会对每一个prop进行 object.is比较 返回true 表示没有变化
PS: 对于引用类型 React只关心引用是否变化
HOOKS
useState
这是React 中的一个hook 函数 它允许我们向组件添加一个状态变脸,从而控制组件的渲染结果

const [msg, setMsg] = useState('')
- useState是一个函数 返回值是一个数组
- 数组中的第一个参数是状态变量,第二个参数是set函数用于修改状态
- useState的参数将作为状态变量的初始值
修改规则
在React 中 状态被认为是只读的 我们应该替换而不是修改 直接修改状态不会得到视图的更新
const [msg, setMsg] = useState('')
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg(msg)
}
//如果是对象作为参数
const [msg, setMsg] = useState({id:'122ds'})
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg({
...msg,
id:'123'})
}
useContext 组件通信
- 使用createContext 方法创建一个上下文对象 ctx=
- 在顶层组件 app 中 通过 ctx.Provider提供数据
- 在底层组件 通过 useContext钩子函数获取消费数据
案例 :

我们需要将app的消息传递到b
const MsgContext = createContext()
function A() {
return <div>this is A
<B/>
</div>
}
function B() {
const msg = useContext(MsgContext)
return <div>this is B from APP:{msg}
</div>
}
function AppDemo() {
const msg = "this is app msg"
return (<div>
<MsgContext.Provider value={msg}>
this is app
<A/>
</MsgContext.Provider>
</div>)
}
useEffect
这是React中的一个 hook 函数 ,用于在React 中创建不是由事件引起而是由渲染本身引起的操作,比如发送 AJAX请求 更改DOM等

基础使用
需求: 在组件渲染完毕后,从服务器获得列表数据展示
语法:
useEffect(()=>{},[])
- 参数1是一个函数,可以把它叫做副作用函数,函数内部可以放置要执行的操作
- 参数2是一个数组 ,数组里放置依赖项,不同依赖项会影响第一个参数的执行,当该参数是一个空数组的时候,副作用函数只会在组件渲染完毕后执行一次
import {useEffect, useState} from "react";
const URL = 'http://geek.itheima.net/v1_0/channels'
function AppDemo() {
const [list, setList] = useState([]);
useEffect(() => {
async function getList() {
const res = await fetch(URL)
const jsonRes = await res.json()
console.log(jsonRes)
setList(jsonRes.data.channels)
}
getList()
console.log("list", list)
}, []);
return (<div>
this is app
<ul>
{list.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>)
}
export default AppDemo;
效果

依赖项参数

function AppDemo() {
/*1. 没有依赖项*/
const [count, setCount] = useState(0);
// useEffect(() => {
// console.log("副作用函数执行了")
// });
/*2 传入空数组依赖*/
// useEffect(() => {
// console.log("副作用函数执行了")
// }, []);
useEffect(() => {
console.log("副作用函数执行了")
}, [count]);
return <div>this is app
<button onClick={() => setCount(count + 1)}>+{count}</button>
</div>
}
清除副作用
在useEffect中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,我们想在组件卸载时把这个定时器清理掉,这个过程就是清理副作用
import {useEffect, useState} from "react";
function Son() {
useEffect(() => {
const timer = setInterval(() => {
console.log("定时器执行中...")
}, 1000)
return () => {
// 清楚副作用
clearInterval(timer)
}
}, []);
return <div>this is son</div>
}
function AppDemo() {
const [show, setShow] = useState(true)
return <div>this is app
{show && <Son/>}
<button onClick={() => setShow(false)}>卸载组件</button>
</div>
}
export default AppDemo;
useReducer
- 定义
redcuer函数 (根据不同的action 返回不同的新状态) - 在组件中调用 useReducer 传入reducer函数和初始状态
- 事件触发的时候,通过 dispatch函数 通过reducer要返回什么状态并且渲染UI
import React, {
useReducer} from 'react';
// 根据不同的case 返回不同的状态
function reducer(state, action) {
switch (action.type) {
case 'INC':
return state + 1
case 'DEC':
return state - 1
case 'SET':
return state = action.payload
default:
return state
}
}
const ReducerDemo = () => {
// 使用 use reducer
const [state, dispatch] = useReducer(reducer, 0)
return (
<div>
<button onClick={
() => dispatch({
type: 'INC'})}>+</button>
{
state}
<button onClick={
() => dispatch({
type: 'DEC'})}>-</button>
<button onClick={
() => dispatch({
type: 'SET', payload: 100})}>Set</button>
</div>
);
};
export default ReducerDemo;
这个钩子相当于 一个可以有多个修改state方法的 usestate
useMemo
作用:它在每次重新渲染的时候能够缓存计算的结果
小案例
- 我们设置一个计算结果的方法 这个方法直接用 大括号的方式渲染
- 设置两个按钮 每次usestate发生变化 都会渲染页面 会导致两个按钮无论点击哪一个都会导致计算结果方法的内容出现变化
import React, {
useState} from 'react';
function factorialOf(n) {
console.log('斐波那契函数执行了')
return n <= 0 ? 1 : n * factorialOf(n - 1)
}
const MemoDemo = () => {
const [count, setCount] = useState(0)
// 计算斐波那契之和
const sumByCount = factorialOf(count)
const [num, setNum] = useState(0)
return (
<>
{
sumByCount}
<button onClick={
() => setCount(count + 1)}>+count:{
count}</button>
<button onClick={
() => setNum(num + 1)}>+num:{
num}</button>
</>
)
};
export default MemoDemo;
useMemo 就是用来解决这种问题的
import React, {
useMemo, useState} from 'react';
function factorialOf(n) {
console.log('斐波那契函数执行了')
return n <= 0 ? 1 : n * factorialOf(n - 1)
}
const MemoDemo = () => {
const [count, setCount] = useState(0)
// 计算斐波那契之和
// const sumByCount = factorialOf(count)
const sumByCount = useMemo(() => {
return factorialOf(count)
}, [count])
const [num, setNum] = useState(0)
return (
<>
{
sumByCount}
<button onClick={
() => setCount(count + 1)}>+count:{
count}</button>
<button onClick={
() => setNum(num + 1)}>+num:{
num}</button>
</>
)
};
export default MemoDemo;
就不会出现 点击num按钮也会触发求和方法情况了
useCallback
作用 在组件多次重新渲染的时候 缓存函数
自定义hook
暂时没有什么很好的例子 写一个比较简单的 之后再拓展
import {useState} from "react";
function useToggle() {
// 可复用代码
const [value, setValue] = useState(true);
const toggle = () => {
setValue(!value)
}
return {value, toggle}
}
function AppDemo() {
const {value, toggle} = useToggle()
return <div>this is app
{value && <div>this is show Toggle</div>}
<button onClick={toggle}>Toggle</button>
</div>
}
export default AppDemo;
效果

点击

Redux
完整代码案例仓库 :https://gitee.com/cold-abyss_admin/react-redux-meituan
Redux是 React 最常用的集中状态管理工具,类似与VUE的pinia(vuex) 可以独立于框架运行

使用思路:
- 定义一个
reducer函数 根据当前想要做的修改返回一个新的状态 - 使用createStore方法传入reducer函数 生成一个store实例对象
- subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
- dispatch方法提交action对象 告诉reducer你想怎么改数据
- getstate方法 获取最新的状态数据更新到视图中

配置Redux
在React中使用redux,官方要求安装俩个其他插件-和react-redux
官方推荐我们使用 RTK(ReduxToolkit) 这是一套工具集合 可以简化书写方式
- 简化store配置
- 内置immer可变式状态修改
- 内置thunk更好的异步创建
调试工具安装
谷歌浏览器搜索 redux-devtool安装 工具
依赖安装
#redux工具包
npm i @reduxjs/toolkit react-redux
#调试工具包
npm install --save-dev redux-devtools-extension

store目录机构设计
- 通常集中状态管理的部分都会单独创建一个
store目录 - 应用通常会有多个子store模块,所以创建一个
modules进行内部业务的区分 - store中的入口文件index.js 的作用是组合所有
modules的子模块 并且导出store

快速上手
使用react+redux 开发一个计数器 熟悉一下技术

-
使用
Reacttoolkit创建 counterStoreimport { createSlice} from "@reduxjs/toolkit"; const counterStore= createSlice({ name: "counter", // 初始化 state initialState: { count: 0 }, // 修改状态的方法 reducers:{ increment(state){ state.count++ }, decrement(state){ state.count-- } } }) // 解构函数 const { increment,decrement}= counterStore.actions // 获取reducer const reducer = counterStore.reducer; export { increment,decrement} export default reducer -
在
index.js集合counterimport { configureStore} from "@reduxjs/toolkit"; import counterStore from "./modules/counterStore"; const store = configureStore({ reducer:{ couner: counterStore, } }) export default store -
为React 注入
store,react-redux负责把Redux和React链接 起来,内置Provider组件 通过store参数把创建好的store实例注入到应用中 找到项目中的index.jsconst root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> ); -
使用useSelector 获取到数据
import {useSelector} from "react-redux"; function App() { const {count} = useSelector(state => state.counter); return ( <div className="App"> {count} </div> ); } -
使用 钩子函数
useDispatchimport { useDispatch, useSelector} from "react-redux"; import { inscrement,descrement} from "./store/modules/counterStore" function App() { const { count} = useSelector(state => state.counter); const dispatch = useDispatch() return ( <div className="App"> <button onClick={ ()=>dispatch(inscrement())}>+</button> { count} <button onClick={ ()=>dispatch(descrement())}>-</button> </div> ); } export default App; -
查看效果

提交acntion传参
在reducers的同步修改方法中添加action对象参数,在调用actionCreater参数的时候传递参数,参数会被传递到action对象的payload属性上
我们继续的改造一下counterStore
action这个对象参数有个固定的属性叫payload用来接收传参

然后 app.js 添加两个按钮 用来传递参数

效果

Reudx action异步操作
区分同步和异步action

如果action的内容是 object对象那就是同步action,如果是函数 那就是异步action
为什么我们需要异步action操作来使用请求 ?
例子:
我们有两种方式可以实现 隔五分钟 上蛋炒饭
一种是客人自己思考五分钟
一种是客人点好 叫服务员五分钟之后上
这个服务员就是 redux 我们刚希望相关aciton的操作都在redux里完成这个时候同步action就不能满足我们的需求了 所以需要使用异步action
异步操作的代码变化不大,我们创建store的写法保持不变 ,但是在函数中用异步操作的时候需要一个能异步执行函数return出一个新的函数而我们的异步操作卸载新的函数中.
异步action中一般都会调用一个同步action
案例: 从后端获取到列表展示到页面
新建一个文件叫做 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
}
}
})
const {
setChannel}= channelStore.actions
// 异步请求
const fetchChannelList = ()=>{
return async (dispatch)=>{
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
dispatch(setChannel(res.data.data.channels))
}
}
const reducer = channelStore.reducer;
export {
fetchChannelList}
export default reducer
然后去store入口加入channelStore
import {
configureStore} from "@reduxjs/toolkit";
import counterStore from "./modules/counterStore";
import channelStore from "./modules/channelStore";
const store = configureStore({
reducer:{
counter: counterStore,
channel: channelStore,
}
})
export default store
之后就可以在app.js加入代码
import {useDispatch, useSelector} from "react-redux";
import {useEffect} from "react";
import {fetchChannelList} from "./store/modules/channelStore";
function App() {
const {channelList} = useSelector(state => state.channel);
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch]);
return (
<div className="App">
<ul>
{channelList.map(item =><li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}
export default App;
代码效果

redux hooks
useSelector
它的作用是吧store中的数据映射到组件中
const {
count} = useSelector(state => state.counter);
这里的count其实对应的就是

useDispatch
它的作用是生成提交 action对象的dispatch函数
import {
useDispatch, useSelector} from "react-redux";
import {
inscrement,descrement} from "./store/modules/counterStore"
function App() {
const {
count} = useSelector(state => state.counter);
const dispatch = useDispatch()
return (
<div className="App">
<button onClick={
()=>dispatch(inscrement())}>+</button>
{
count}
<button onClick={
()=>dispatch(descrement())}>-</button>
</div>
);
}
export default App;
美团点餐界面小案例
下载模板地址:
git clone http://git.itcast.cn/heimaqianduan/redux-meituan.git
效果与功能列表展示

基本的思路就是使用 RTK 来做状态管理,组件负责数据渲染和操作action

我们在store文件夹下开始配置和编写store的使用逻辑
分类渲染
先编写对应的reducer 和异步请求逻辑
takeaway.js
用于异步请求列表数据
import {
createStore} from './store';
import axios from "axios";
const foodsState = createStore({
name:'foods',
initialState: {
foodsList:[]
},
reducers:{
setFoodsList(state, action){
state.foodsList=action.payload
}
}
});
const {
setFoodsList} = foodsState.actions;
//异步获取部分
const fetchFoodsList = () => {
return async dispatch => {
// 异步逻辑
const res = await axios.get(' http://localhost:3004/takeaway\n')
// 调用dispatch
dispatch(setFoodsList(res.data))
}
}
const reducer = foodsState.reducer
export {
fetchFoodsList}
export default reducer
将子store管理起来 在store文件夹下编写一个index.js作为访问store的入口
import {
configureStore} from "@reduxjs/toolkit";
import foodsReducer from './modules/takeaway'
const store= configureStore({
reducer:{
foods:foodsReducer
}
})
export default store
然后将redux和react连接起来 将store 注入进去 选择根目录的index.js
import React from 'react'
import { 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(
<Provider store={store}>
<App />
</Provider>
)
编写渲染页面
在app.js里 遵循步骤开始操作store
- 使用
useDispatch函数取得对象 - 使用
useEffect调用异步函数获取服务器数据 - 使用
useSelector拿到数据并且循环展示
import NavBar from './components/NavBar'
import Menu from './components/Menu'
import Cart from './components/Cart'
import FoodsCategory from './components/FoodsCategory'
import './App.scss'
import {useSelector} from "react-redux";
const App = () => {
// 访问store拿到数据
const {foodsList} = useSelector(state => state.foods)
return (
<div className="home">
{/* 导航 */}
<NavBar />
{/* 内容 */}
<div className="content-wrap">
<div className="content">
<Menu />
<div className="list-content">
<div className="goods-list">
{/* 外卖商品列表 */}
{foodsList.map(item => {
return (
<FoodsCategory
key={item.tag}
// 列表标题
name={item.name}
// 列表商品
foods={item.foods}
/>
)
})}
</div>
</div>
</div>
</div>
{/* 购物车 */}
<Cart />
</div>
)
}
export default App
效果

侧边栏渲染.交互
我们需要在获取列表解构的时候 拿到属于左侧列表的数据

然后循环的展示在menu组件中 只需要把异步请求的数据放到menu组件中就可以展示侧边栏了
import classNames from 'classnames'
import './index.scss'
import {
useDispatch, useSelector} from "react-redux";
const Menu = () => {
// 获取dispatch
const dispatch = useDispatch()
// 访问store拿到数据
const {
foodsList} = useSelector(state => state.foods)
const menus = foodsList.map(item => ({
tag: item.tag, name: item.name }))
return (
<nav className="list-menu">
{
/* 添加active类名会变成激活状态 */}
{
menus.map((item, index) => {
return (
<div
key={
item.tag}
className={
classNames(
'list-menu-item',
'active'
)}
>
{
item.name}
</div>
)
})}
</nav>
)
}
export default Menu
效果

接下来编写交互操作 使用RTK来管理activeindex
- 新增
activeIndex并且设置好对应的同步操作action方法以及导出
import {
createSlice} from '@reduxjs/toolkit';
import axios from "axios";
const foodsState = createSlice({
name:'foods',
initialState: {
// 商品列表
foodsList:[],
// 菜单激活值
activeIndex:0,
},
reducers:{
setFoodsList(state, action){
state.foodsList=action.payload
},
changeActiveIndex(state, action){
state.activeIndex=action.payload
}
}
});
const {
setFoodsList,changeActiveIndex} = foodsState.actions;
//异步获取部分
const fetchFoodsList = () => {
return async dispatch => {
// 异步逻辑
const res = await axios.get(' http://localhost:3004/takeaway\n')
// 调用dispatch
dispatch(setFoodsList(res.data))
console.log(res.data)
}
}
const reducer = foodsState.reducer
export {
fetchFoodsList,changeActiveIndex}
export default reducer
然后开始编写menu组件的点击效果

代码修改
menu/index.js
import classNames from 'classnames'
import './index.scss'
import {
useDispatch, useSelector} from "react-redux";
import {
changeActiveIndex} from "../../store/modules/takeaway";
const Menu = () => {
// 获取dispatch
const dispatch = useDispatch()
// 访问store拿到数据
const {
foodsList,activeIndex} = useSelector(state => state.foods)
const menus = foodsList.map(item => ({
tag: item.tag, name: item.name }))
return (
<nav className="list-menu">
{
/* 添加active类名会变成激活状态 */}
{
menus.map((item, index) => {
return (
<div
onClick={
()=>dispatch(changeActiveIndex(index))}
key={
item.tag}
className={
classNames(
'list-menu-item',
activeIndex===index&& 'active'
)}
>
{
item.name}
</div>
)
})}
</nav>
)
}
export default Menu
效果
当点击的时候index就会切换到对应的index上 并且在点击当前index的时候选项高亮


商品列表的切换显示
点击侧边栏的时候 菜单栏需要显示对应侧边栏index的菜单
修改 app.js菜单栏标签的显示规则就行
const App = () => {


最低0.47元/天 解锁文章
1378

被折叠的 条评论
为什么被折叠?



