退出登录的功能示例
javascript
import React, { FC, useEffect } from 'react'
import { Button, message } from 'antd'
import { Link, useNavigate } from 'react-router-dom'
import { UserOutlined } from '@ant-design/icons'
import { useDispatch } from 'react-redux'
// import { useRequest } from 'ahooks'
import { LOGIN_PATHNAME } from '../router'
// import { getUserInfoService } from '../services/user'
import { removeToken } from '../utils/user-token'
import useGetUserInfo from '../hooks/useGetUserInfo'
import { logoutReducer } from '../store/userReducer'
const UserInfo: FC = () => {
const nav = useNavigate()
const dispatch = useDispatch()
// const { data } = useRequest(getUserInfoService) // ajax
// const { username, nickname } = data || {}
const { username, nickname } = useGetUserInfo() // 从 redux 中获取用户信息
function logout() {
dispatch(logoutReducer()) // 清空了 redux user 数据
removeToken() // 清除 token 的存储
message.success('退出成功')
}
// 监听用户状态变化,如果用户已退出则跳转到登录页
useEffect(() => {
if (!username) {
nav(LOGIN_PATHNAME)
}
}, [username, nav])
const UserInfo = (
<>
<span style={{ color: '#e8e8e8' }}>
<UserOutlined />
{nickname}
</span>
<Button type="link" onClick={logout}>
退出
</Button>
</>
)
const Login = <Link to={LOGIN_PATHNAME}>登录</Link>
return <div>{username ? UserInfo : Login}</div>
}
export default UserInfo
用法说明
这个 useEffect 是用来监听用户登出状态并执行相应跳转的核心逻辑。让我详细解释一下它是如何工作的:
javascript
useEffect(() => {
if (!username) {
nav(LOGIN_PATHNAME)
}
}, [username, nav])
这段代码的作用是:
- 监听依赖项 :第二个参数
[username, nav]表示这个 effect 依赖于username和nav这两个变量 - 触发条件 :只有当
username或nav的值发生变化时,才会重新执行 effect 中的函数 - 执行逻辑 :
- 检查 username 是否为空(falsy值)
- 如果为空,就调用
nav(LOGIN_PATHNAME)跳转到登录页面
具体流程
当你点击"退出"按钮时:
- 触发 logout 函数
- logout 函数中执行
dispatch(logoutReducer())- logoutReducer 会把 Redux store 中的用户信息重置为初始状态
{ username: '', nickname: '' }- 由于 useGetUserInfo() hook 会订阅 Redux store 中的用户信息,所以 username 变量会从实际用户名变为 ' '
useEffect检测到 username 发生了变化,于是重新执行其中的函数- 此时
!username为 true(因为 username 是空字符串),所以执行nav(LOGIN_PATHNAME)- 页面跳转到登录页
/login
订阅模式介绍
在 React 和 Redux 中,"订阅"是一种设计模式,用于监听状态变化并在状态更新时自动通知相关组件。让我详细解释一下这个过程:
Redux 订阅机制的工作原理
Redux 使用发布-订阅(Publish-Subscribe)模式来实现状态变化的通知机制。下面是具体的工作流程:
`
- 订阅建立:当组件使用 useGetUserInfo() 这样的自定义 hook 时,它内部会使用 react-redux 提供的 useSelector hook 来订阅 Redux store 中的特定状态。
- 状态变化检测:每当 dispatch 一个 action(例如 logoutReducer())时,Redux 会更新 store 中的状态。
- 通知机制:Redux 会比较更新前后的状态,如果发现有变化,就会通知所有订阅了这部分状态的组件。
- 组件重新渲染:接收到通知的组件会使用新的状态值重新渲染。`
具体到我们的例子
javascript
// useGetUserInfo hook 内部大概是这样的
export default function useGetUserInfo() {
const { username, nickname } = useSelector<StateType>(state => state.user)
return { username, nickname }
}
// 在 UserInfo 组件中
const { username, nickname } = useGetUserInfo()
当执行 dispatch(logoutReducer()) 时:
- Redux store 中的 user 状态从
{ username: 'actual_username', nickname: 'actual_nickname' }变成了{ username: '', nickname: '' } - Redux 检测到这个变化
- 所有使用 useSelector 订阅了
state.user的组件都会收到通知 - UserInfo 组件重新渲染,此时 username 变量的值变成了 ' '
useEffect 如何响应变化
javascript
useEffect(() => {
if (!username) {
nav(LOGIN_PATHNAME)
}
}, [username, nav])
这里的 [username, nav] 是依赖数组,告诉 React:"当 username 或 nav 变化时,请重新执行这个 effect"。
所以整个流程是:
用户点击退出按钮
dispatch logoutReducer() 清空用户信息
Redux 通知订阅了用户信息的所有组件
UserInfo 组件重新渲染,username 更新为空字符串
useEffect 检测到 username 变化,执行回调函数
因为 !username 为 true,执行跳转到登录页
这就是所谓的"订阅模式"------组件订阅了状态的一部分,当这部分状态变化时,组件会自动得到通知并做出响应。这是一种非常高效且常用的 React 状态管理模式。
Effect的概念
Effect 是 React 中的一个核心概念,指的是在组件渲染之外执行的操作。这些操作通常包括:
- 数据获取(API 请求)
- 订阅(如 WebSocket 连接)
- 手动修改 DOM
- 定时器(setTimeout, setInterval)
- 日志记录
- 页面跳转(路由导航)
在传统的 Class 组件中,这些操作通常在 componentDidMount、componentDidUpdate 和 componentWillUnmount 等生命周期方法中处理。
useEffect Hook
React Hooks 引入了 useEffect 来统一处理这些副作用。它的基本语法是:
javascript
useEffect(() => {
// 副作用代码
// 可选的清理函数
return () => {
// 清理代码
};
}, [/* 依赖数组 */]);
这里的 effect 做的事情是:
- 副作用操作 :页面跳转 (
nav(LOGIN_PATHNAME)) - 触发条件:当 username 变为空时
- 依赖项:username 和 nav
为什么叫"Effect"
之所以叫做"effect",是因为它处理的是"副作用",即不是直接参与渲染过程,但却会产生重要影响的操作。在函数式编程中,纯函数不应该有任何副作用,而 effect 正是用来处理这些不可避免的副作用的机制。
Effect 的工作机制
- 初始化:组件首次渲染后,执行 effect 函数(如果提供了依赖数组且为空,则只执行一次)
- 依赖更新:当依赖数组中的任何一个值发生变化时,重新执行 effect 函数
- 清理:如果 effect 函数返回了一个清理函数,它会在下一次 effect 执行前或者组件卸载时被调用
在我们的例子中:
- 当用户点击退出按钮,触发 logout
- logout 清除了 Redux 中的用户信息
- username 从真实用户名变成空字符串
- useEffect 检测到 username 变化,执行 effect 函数
- effect 函数发现
!username为真,执行页面跳转
这就是 effect 的完整工作流程:监听特定状态变化,当变化发生时执行相应的副作用操作。