Redux入门:美团购物车列表小案例

引言

📒📒📒欢迎来到小冷的代码学习世界
博主的微信公众号想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
当前专栏react系列

Redux

完整代码案例仓库 :https://gitee.com/cold-abyss_admin/react-redux-meituan

Redux是 React 最常用的集中状态管理工具,类似与VUE的pinia(vuex) 可以独立于框架运行

image-20241211182447283

使用思路:

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

image-20241211185420416

配置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

image-20241213153131189

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

image-20241211190849933

快速上手

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

image-20241211190956868

  1. 使用 Reacttoolkit 创建 counterStore

    import {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
    
  2. index.js集合counter

    import {configureStore} from "@reduxjs/toolkit";
    import counterStore from "./modules/counterStore";
    const store = configureStore({
        reducer:{
            couner: counterStore,
        }
    })
    
    export default store
    
  3. 为React 注入store, react-redux负责把Redux和React链接 起来,内置 Provider组件 通过 store 参数把创建好的store实例注入到应用中 找到项目中的index.js

    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
          <Provider store={store}>
              <App />
          </Provider>
      </React.StrictMode>
    );
    
    
  4. 使用useSelector 获取到数据

    import {useSelector} from "react-redux";
    
    function App() {
      const {count} = useSelector(state => state.counter);
      return (
        <div className="App">
          {count}
        </div>
      );
    }
    
  5. 使用 钩子函数 useDispatch

    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;
    
    
  6. 查看效果

    image-20241211194212353

提交acntion传参

reducers的同步修改方法中添加action对象参数,在调用actionCreater参数的时候传递参数,参数会被传递到action对象的payload属性上

我们继续的改造一下counterStore

action这个对象参数有个固定的属性叫payload用来接收传参

image-20241212183228071

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

image-20241212183251405

效果

image-20241212183353807

Reudx action异步操作

区分同步和异步action

image-20241212192017617

如果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;

代码效果

image-20241212191240289

redux hooks

useSelector

它的作用是吧store中的数据映射到组件中

 const {count} = useSelector(state => state.counter);

这里的count其实对应的就是

image-20241211192355704

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

效果与功能列表展示

image-20241213143205796

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

image-20241213144807698

我们在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

  1. 使用useDispatch函数取得对象
  2. 使用 useEffect 调用异步函数获取服务器数据
  3. 使用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

效果

image-20241213145927894

侧边栏渲染.交互

我们需要在获取列表解构的时候 拿到属于左侧列表的数据

image-20241213150225046

然后循环的展示在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

效果

image-20241213151100641

接下来编写交互操作 使用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组件的点击效果

image-20241213151806359

代码修改 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的时候选项高亮

image-20241213153536781

image-20241213153228472

商品列表的切换显示

点击侧边栏的时候 菜单栏需要显示对应侧边栏index的菜单

修改 app.js菜单栏标签的显示规则就行

const App = () => {
 //    获取dispatch
 const  dispatch = useDispatch()
  //   异步请求数据
  useEffect(() => {
    dispatch(fetchFoodsList())
  }, [dispatch]);
 // 访问store拿到数据
 const {foodsList,activeIndex} = 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,index) => {
                return (
                index===activeIndex&&  <FoodsCategory
                    key={item.tag}
                    // 列表标题
                    name={item.name}
                    // 列表商品
                    foods={item.foods}
                  />
                )
              })}
            </div>
          </div>
        </div>
      </div>

      {/* 购物车 */}
      <Cart />
    </div>
  )
}
添加购物车

首先找到fooditem中的food对象 一会我们使用cartlist的时候要用到 id 和count

image-20241213155814881

使用 RTK管理 状态cartlist

import {createSlice} from '@reduxjs/toolkit';
import axios from "axios";
const foodsState = createSlice({
    name:'foods',
    initialState: {
        // 商品列表
        foodsList:[],
     // 菜单激活值
        activeIndex:0,
        // 购物车列表
        cartList:[]
    },
    reducers:{
        // 修改商品列表
        setFoodsList(state, action){
            state.foodsList=action.payload
        },
        // 更改activeIndex
        changeActiveIndex(state, action){
            state.activeIndex=action.payload
        },
        // 添加购物车
        addCart(state, action){
        //    通过payload.id去匹配cartList匹配,匹配到代表添加过
           const  item = state.cartList.find(item=>item.id ===action.payload.id)
            if (item){
                item.count++
            }else{
                state.cartList.push(action.payload)
            }
        }
    }
});
const {setFoodsList,changeActiveIndex,addCart} = 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,addCart}
export default reducer

fooditem.jsx编写cartList触发操作

  1. 要记得给 count一个默认值 不然会是 null
  2. 修改 classname为plus的span标签新增点击事件
import './index.scss'
import {useDispatch} from "react-redux";
import {addCart} from "../../../store/modules/takeaway";

const Foods = ({
  id,
  picture,
  name,
  unit,
  description,
  food_tag_list,
  month_saled,
  like_ratio_desc,
  price,
  tag,
  count =1
}) => {
  const  dispatch = useDispatch()
  return (
    <dd className="cate-goods">
      <div className="goods-img-wrap">
        <img src={picture} alt="" className="goods-img" />
      </div>
      <div className="goods-info">
        <div className="goods-desc">
          <div className="goods-title">{name}</div>
          <div className="goods-detail">
            <div className="goods-unit">{unit}</div>
            <div className="goods-detail-text">{description}</div>
          </div>
          <div className="goods-tag">{food_tag_list.join(' ')}</div>
          <div className="goods-sales-volume">
            <span className="goods-num">月售{month_saled}</span>
            <span className="goods-num">{like_ratio_desc}</span>
          </div>
        </div>
        <div className="goods-price-count">
          <div className="goods-price">
            <span className="goods-price-unit">¥</span>
            {price}
          </div>
          <div className="goods-count">
            <span className="plus" onClick={()=>{dispatch(addCart({
              id,
              picture,
              name,
              unit,
              description,
              food_tag_list,
              month_saled,
              like_ratio_desc,
              price,
              tag,
              count
            }))}}></span>
          </div>
        </div>
      </div>
    </dd>
  )
}

export default Foods

效果

image-20241213161048251

统计订单区域

image.png

实现思路

  1. 基于store中的cartList的length渲染数量
  2. 基于store中的cartList累加price * count
  3. 购物车cartList的length不为零则高亮
  4. 设置总价
// 计算总价 
const totalPrice = cartList.reduce((a, c) => a + c.price * c.count, 0)

{/* fill 添加fill类名购物车高亮*/}
{/* 购物车数量 */}
<div onClick={onShow} className={classNames('icon', cartList.length > 0 && 'fill')}>
  {cartList.length > 0 && <div className="cartCornerMark">{cartList.length}</div>}
</div>

image-20241213182102778

效果

image-20241213182156616

cart.jsx全部代码

import classNames from 'classnames'
import Count from '../Count'
import './index.scss'
import {useSelector} from "react-redux";
import {fill} from "lodash/array";

const Cart = () => {
    const{cartList} = useSelector(state => state.foods)
  //   计算总价
   const  totalPrice = cartList.reduce((a, c) => a+c.price*c.count,0)
  const cart = []
  return (
    <div className="cartContainer">
      {/* 遮罩层 添加visible类名可以显示出来 */}
      <div
        className={classNames('cartOverlay')}
      />
      <div className="cart">
        {/* fill 添加fill类名可以切换购物车状态*/}
        {/* 购物车数量 */}
        <div className={classNames('icon')}>
          {cartList.length>0 && <div className="cartCornerMark">{cartList.length}</div>}
        </div>
        {/* 购物车价格 */}
        <div className="main">
          <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
              {totalPrice.toFixed(2)}
            </span>
          </div>
          <span className="text">预估另需配送费 ¥5</span>
        </div>
        {/* 结算 or 起送 */}
        {cartList.length > 0 ? (
          <div className="goToPreview">去结算</div>
        ) : (
          <div className="minFee">¥20起送</div>
        )}
      </div>
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel')}>
        <div className="header">
          <span className="text">购物车</span>
          <span className="clearCart">
            清空购物车
          </span>
        </div>

        {/* 购物车列表 */}
        <div className="scrollArea">
          {cart.map(item => {
            return (
              <div className="cartItem" key={item.id}>
                <img className="shopPic" src={item.picture} alt="" />
                <div className="main">
                  <div className="skuInfo">
                    <div className="name">{item.name}</div>
                  </div>
                  <div className="payableAmount">
                    <span className="yuan">¥</span>
                    <span className="price">{item.price}</span>
                  </div>
                </div>
                <div className="skuBtnWrapper btnGroup">
                  <Count
                    count={item.count}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart

购物车列表功能

image-20241213182736757

修改takeaway.js内容如下 :

  • 新增加减购物车内的视频数量
  • 清楚购物车
  • 只有一项时删除商品选择
import {createSlice} from '@reduxjs/toolkit';
import axios from "axios";
const foodsState = createSlice({
    name:'foods',
    initialState: {
        // 商品列表
        foodsList:[],
     // 菜单激活值
        activeIndex:0,
        // 购物车列表
        cartList:[]
    },
    reducers:{
        // 修改商品列表
        setFoodsList(state, action){
            state.foodsList=action.payload
        },
        // 更改activeIndex
        changeActiveIndex(state, action){
            state.activeIndex=action.payload
        },
        // 添加购物车
        addCart(state, action){
        //    通过payload.id去匹配cartList匹配,匹配到代表添加过
           const  item = state.cartList.find(item=>item.id ===action.payload.id)
            if (item){
                item.count++
            }else{
                state.cartList.push(action.payload)
            }
        },
    //     count增
        increCount(state, action){
            const  item = state.cartList.find(item=>item.id ===action.payload.id)
            item.count++
        },
    //     count减
        decreCount(state, action){
            const  item = state.cartList.find(item=>item.id ===action.payload.id)
             // 只有一项的时候将商品移除购物车
            if (item.count <=1){
                state.cartList=  state.cartList.filter(item=>item.id !=action.payload.id)
                return
            }
            item.count--
        },
    //     清除购物车
        clearCart(state){
            state.cartList=[]
        }
    }
});
const {clearCart,decreCount,increCount,setFoodsList,changeActiveIndex,addCart} = 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,addCart,clearCart,decreCount,increCount}
export default reducer

购物车列表的显示和隐藏

image-20241213185233627

  • 使用usestate设置一个状态
  • 点击统计的时候就展示
  • 点击蒙层就不显示
import classNames from 'classnames'
import Count from '../Count'
import './index.scss'
import {useDispatch, useSelector} from "react-redux";
import {clearCart, decreCount, increCount} from "../../store/modules/takeaway";
import {useState} from "react";

const Cart = () => {
    const  dispatch =useDispatch()
    const{cartList} = useSelector(state => state.foods)
  //   计算总价
   const  totalPrice = cartList.reduce((a, c) => a+c.price*c.count,0)
 const[visible,setVisible]=useState(false)
  return (
    <div className="cartContainer">
      {/* 遮罩层 添加visible类名可以显示出来 */}
      <div
          onClick={()=>setVisible(false)}
        className={classNames('cartOverlay',visible&&'visible')}
      />
      <div className="cart">
        {/* fill 添加fill类名可以切换购物车状态*/}
        {/* 购物车数量 */}
        <div onClick={()=>setVisible(cartList.length!=0)} className={classNames('icon')}>
          {cartList.length>0 && <div className="cartCornerMark">{cartList.length}</div>}
        </div>
        {/* 购物车价格 */}
        <div className="main">
          <div className="price">
            <span className="payableAmount">
              <span className="payableAmountUnit">¥</span>
              {totalPrice.toFixed(2)}
            </span>
          </div>
          <span className="text">预估另需配送费 ¥5</span>
        </div>
        {/* 结算 or 起送 */}
        {cartList.length > 0 ? (
          <div className="goToPreview">去结算</div>
        ) : (
          <div className="minFee">¥20起送</div>
        )}
      </div>
      {/* 添加visible类名 div会显示出来 */}
      <div className={classNames('cartPanel',visible&&'visible')}>
        <div className="header">
          <span className="text">购物车</span>
          <span onClick={()=>dispatch(clearCart())} className="clearCart">
            清空购物车
          </span>
        </div>

        {/* 购物车列表 */}
        <div className="scrollArea">
          {cartList.map(item => {
            return (
              <div className="cartItem" key={item.id}>
                <img className="shopPic" src={item.picture} alt="" />
                <div className="main">
                  <div className="skuInfo">
                    <div className="name">{item.name}</div>
                  </div>
                  <div className="payableAmount">
                    <span className="yuan">¥</span>
                    <span className="price">{item.price}</span>
                  </div>
                </div>
                <div className="skuBtnWrapper btnGroup">
                  <Count
                      onPlus={()=>dispatch(increCount({id:item.id}))}
                    count={item.count}
                      onMinus={()=>dispatch(decreCount({id:item.id}))}
                  />
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </div>
  )
}

export default Cart

到这里redux的入门, 实践, 小案例就完成了 之后可能会更新一些关于redux底层原理的文章 会加入到其中

### React 中 `TypeError: Cannot read properties of undefined (reading 'map')` 错误解决方案 在处理React应用中的数据时,如果尝试对未初始化或不存在的对象属性调用方法,则会触发此类型的错误。具体到美团案例中,当试图使用`.map()`函数遍历一个尚未被赋值为数组的变量时,就会抛出`Uncaught TypeError: Cannot read properties of undefined (reading 'map')`异常[^1]。 为了避免这种情况发生,在JSX模板内渲染列表之前应该先验证目标对象是否确实是一个有效的数组实例。一种常见做法是利用逻辑运算符&&来确保只有在其左侧表达式的求值结果为真(即不是null, undefined等假值)的情况下才会继续执行右侧的操作: ```javascript {channelList && channelList.map(item => ( <Option key={item.id} value={item.id}> {item.name} </Option> ))} ``` 上述代码片段展示了如何安全地迭代并显示来自`channelList`的数据项而不会引起运行期崩溃。通过这种方式可以有效防止因意外访问到了未定义状态下的成员而导致程序中断的情况出现[^4]。 另外还可以考虑采用可选链操作符(`?.`)来进行更简洁的安全检查: ```javascript {channelList?.map(item => ( <Option key={item.id} value={item.id}> {item.name} </Option> ))} ``` 这里使用的问号表示如果前面的部分存在则继续计算后面的路径;否则整个表达式的结果将是undefined而不是抛出错误。这同样能够达到保护目的的同时让语法更加直观易读[^2]。 最后值得注意的是应当始终关注组件接收到props的时间点以及其生命周期内的变化情况。有时候问题可能源于父级传递下来的prop未能及时更新或是异步加载过程中产生的临时空白期所造成。因此除了即时性的防御措施外还需要从根本上排查是否存在延迟或其他潜在影响因素使得预期中的数组变成了undefined的状态[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷环渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值