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会改变。
在你给出的代码中,我看不出token改变的原因,或者你可以深入调查一下。 这个问题之前一直没看到,拖了这么久才回复,实在不好意思,
00
相似问题
函数组件和类组件
回答 1
不太理解传递子组件传参的方式
回答 1