dispatch(getShoppingCart(token) 写在 Header 组件中会无限请求api

来源:12-5 【redux连接】加载购物车

王鹳厶

2023-09-13

文件路径: src\components\header\Header.tsx

import React, { useEffect, useState } from 'react'
import styles from './Header.module.css'
import logo from 'assets/images/logo.svg'
import type { MenuProps } from 'antd'
import { ConfigProvider, Layout, Typography, Input, Menu, Button, Dropdown } from 'antd'
import { GlobalOutlined } from '@ant-design/icons'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import { useAppSelector, useAppDispatch } from 'redux/hook'
import { changeLanguageActionCreator } from 'redux/actions/language' // 经典 redux
import { searchTouristRoutes } from 'redux/slice/searchKeyword' // RTK
import { getShoppingCart } from 'redux/slice/shoppingCart' // RTK
import { logout } from 'redux/slice/user' // RTK
import jwt_decode from 'jwt-decode' // 后端只返回了一个token字符串,所以需要安装jwt反译插件获取username
import type { JwtPayload } from 'jwt-decode'

interface Token extends JwtPayload {
  username: string
}

const Header: React.FC = () => {
  const navigate = useNavigate()
  const { t } = useTranslation()
  const { language, languageList } = useAppSelector((state) => state.language)
  const { keyword } = useAppSelector((state) => state.searchKeyword)
  const { token } = useAppSelector((state) => state.user)
  const {
    loading: shoppingCartLoading,
    error: shoppingCartError,
    data: shoppingCartData,
  } = useAppSelector((state) => state.shoppingCart)
  const dispatch = useAppDispatch()
  const [username, setUsername] = useState<string>('')

  useEffect(() => {
    if (token) {
      // https://jwt.io/ decode 的信息中没有 username 了, 这里自己加上!!
      setUsername(jwt_decode<Token>(token)?.username || 'alex1234@163.com')
      // todo 为什么写在Header组件里会无限循环执行?
      dispatch(getShoppingCart(token)) // 老师好, 写在这里, 页面无限请求!! 为什么呢?
    }
  }, [token])

  const items: MenuProps['items'] = languageList.map((item) => ({ key: item.code, label: item.name }))
  const menus: MenuProps['items'] = [
    { key: '1', label: t('header.home_page') },
    { key: '2', label: t('header.weekend') },
    { key: '3', label: t('header.group') },
    { key: '4', label: t('header.backpack') },
    { key: '5', label: t('header.private') },
    { key: '6', label: t('header.cruise') },
    { key: '7', label: t('header.hotel') },
    { key: '8', label: t('header.local') },
    { key: '9', label: t('header.theme') },
    { key: '10', label: t('header.custom') },
    { key: '11', label: t('header.study') },
    { key: '12', label: t('header.visa') },
    { key: '13', label: t('header.enterprise') },
    { key: '14', label: t('header.high_end') },
    { key: '15', label: t('header.outdoor') },
    { key: '16', label: t('header.insurance') },
  ]

  const handleMenuClick: MenuProps['onClick'] = (e) => {
    // console.log('e----', e)
    // console.log('dispatch-----', dispatch)
    dispatch(changeLanguageActionCreator(e.key as 'zh' | 'en'))
  }

  return (
    <div className={styles['app-header']}>
      {/* header1 */}
      <div className={styles['header1']}>
        <div className={styles['header1-inner']}>
          <Typography.Text style={{ minWidth: 145 }}>{t('header.slogan')}</Typography.Text>
          <Dropdown.Button
            style={{ marginLeft: 5 }}
            icon={<GlobalOutlined />}
            menu={{ items, onClick: handleMenuClick }}
          >
            {languageList.find((item) => item.code === language)?.name || '未知'}
          </Dropdown.Button>
          {token ? (
            <Button.Group className={styles['button-group']}>
              <Button loading={shoppingCartLoading} onClick={() => navigate('/shopping-cart')}>
                {shoppingCartError ? (
                  '购物车数据加载失败'
                ) : shoppingCartLoading ? (
                  '购物车数据加载中...'
                ) : (
                  <>
                    <span>{t('header.shoppingCart')}</span>
                    <span>({shoppingCartData.shoppingCartItems?.length})</span>
                  </>
                )}
              </Button>
              <Button
                onClick={() => {
                  dispatch(logout())
                  navigate('/')
                  // window.location.reload() // 刷新会导致 redux 运行不完整, 用户无法logout
                }}
              >
                {t('header.signOut')}
              </Button>
              <span style={{ marginLeft: 15, width: 220 }}>
                {t('header.welcome')}
                {'  '}
                <Typography.Text strong>{username}</Typography.Text>
              </span>
            </Button.Group>
          ) : (
            <Button.Group className={styles['button-group']}>
              <Button onClick={() => navigate('/registry')}>{t('header.register')}</Button>
              <Button onClick={() => navigate('/login')}>{t('header.signin')}</Button>
            </Button.Group>
          )}
        </div>
      </div>
      {/* header2 */}
      <Layout.Header className={styles['header2']}>
        <span className={styles['logo-box']} onClick={() => navigate('/')}>
          <img src={logo} alt="logo" className={styles['App-logo']} />
          <Typography.Title level={3} className={styles.title}>
            {t('header.title')}
          </Typography.Title>
        </span>
        <Input.Search
          placeholder={t('header.search_input_placeholder')}
          className={styles['search-input']}
          defaultValue={keyword}
          onSearch={(value: string, event) => {
            // console.log('event---', event)
            dispatch(searchTouristRoutes(value))
            navigate(`/search`)
          }}
        />
      </Layout.Header>
      {/* header3 antd@5 样式用 js 写进去的, 即用css样式覆盖方式修改样式无效!!! */}
      <ConfigProvider
        theme={{
          components: {
            Menu: {
              colorBgContainer: '#1976D2',
              colorText: '#f5f5f5',
              colorPrimary: 'yellow',
            },
          },
        }}
      >
        <Menu mode="horizontal" className={styles['header3']} items={menus} defaultSelectedKeys={['1']} />
      </ConfigProvider>
    </div>
  )
}

Header.whyDidYouRender = true

export { Header }

图片描述

写回答

1回答

阿莱克斯刘

2024-03-26

我觉得应该重点关注一下变量token,因为当token改变的时候useeffect语句会执行。那么,我们就要检查一下为什么token会改变。

https://img1.sycdn.imooc.com/szimg/66021234096949fc15000436.jpg

在你给出的代码中,我看不出token改变的原因,或者你可以深入调查一下。 这个问题之前一直没看到,拖了这么久才回复,实在不好意思,

0
0

React18 系统精讲 结合TS打造旅游电商平台

React18 精讲 + 结合 TS 实战 + 热门业务开发,获取必备技能

1993 学习 · 1015 问题

查看课程