目录

useDispatch 的基本概念

主要用途

在我们的项目中的应用


useDispatch 的基本概念

useDispatch 是一个 React Hook,它返回 Redux store 的 dispatch 函数的引用。通过这个函数,我们可以向 Redux store 发送 actions,从而更新应用程序的状态。

主要用途

  1. 触发 Redux actions:通过 useDispatch 获取 dispatch 函数,然后用它来 dispatch actions,更新 Redux store 中的状态。

  2. 连接 UI 与状态管理:允许组件通过 dispatching actions 来响应用户交互或其它事件。

在我们的项目中的应用

在 Login.tsx 组件中,我们这样使用了 useDispatch:

import React, { FC, useEffect } from 'react'
import { useNavigate, Link } from 'react-router-dom'
import { Typography, Space, Form, Input, Button, Checkbox, message } from 'antd'
import { UserAddOutlined } from '@ant-design/icons'
import { useDispatch } from 'react-redux'
import { useRequest } from 'ahooks'
import { REGISTER_PATHNAME, MANAGE_INDEX_PATHNAME } from '../router'
import { loginService } from '../services/user'
import { setToken } from '../utils/user-token'
import { loginReducer } from '../store/userReducer'
import styles from './Login.module.scss'

const { Title } = Typography

const USERNAME_KEY = 'USERNAME'
const PASSWORD_KEY = 'PASSWORD'

function rememberUser(username: string, password: string) {
  localStorage.setItem(USERNAME_KEY, username)
  localStorage.setItem(PASSWORD_KEY, password)
}

function deleteUserFromStorage() {
  localStorage.removeItem(USERNAME_KEY)
  localStorage.removeItem(PASSWORD_KEY)
}

function getUserInfoFromStorage() {
  return {
    username: localStorage.getItem(USERNAME_KEY),
    password: localStorage.getItem(PASSWORD_KEY),
  }
}

const Login: FC = () => {
  const nav = useNavigate()
  const dispatch = useDispatch()

  const [form] = Form.useForm() // 第三方 hook

  useEffect(() => {
    const { username, password } = getUserInfoFromStorage()
    form.setFieldsValue({ username, password })
  }, [])

  const { run } = useRequest(
    async (username: string, password: string) => {
      const data = await loginService(username, password)
      return data
    },
    {
      manual: true,
      onSuccess(result) {
        const { token = '', userInfo } = result
        setToken(token) // 存储 token

        // 主动将用户信息存入 Redux store
        const { username, nickname } = userInfo
        dispatch(loginReducer({ username, nickname }))

        message.success('登录成功')
        nav(MANAGE_INDEX_PATHNAME) // 导航到"我的问卷"
      },
    }
  )

  const onFinish = (values: any) => {
    const { username, password, remember } = values || {}

    run(username, password) // 执行 ajax

    if (remember) {
      rememberUser(username, password)
    } else {
      deleteUserFromStorage()
    }
  }

  return (
    <div className={styles.container}>
      <div>
        <Space>
          <Title level={2}>
            <UserAddOutlined />
          </Title>
          <Title level={2}>用户登录</Title>
        </Space>
      </div>
      <div>
        <Form
          labelCol={{ span: 6 }}
          wrapperCol={{ span: 16 }}
          initialValues={{ remember: true }}
          onFinish={onFinish}
          form={form}
        >
          <Form.Item
            label="用户名"
            name="username"
            rules={[
              { required: true, message: '请输入用户名' },
              { type: 'string', min: 5, max: 20, message: '字符长度在 5-20 之间' },
              { pattern: /^\w+$/, message: '只能是字母数字下划线' },
            ]}
          >
            <Input />
          </Form.Item>
          <Form.Item
            label="密码"
            name="password"
            rules={[{ required: true, message: '请输入密码' }]}
          >
            <Input.Password />
          </Form.Item>
          <Form.Item name="remember" valuePropName="checked" wrapperCol={{ offset: 6, span: 16 }}>
            <Checkbox {...({ children: '记住我' } as any)} />
          </Form.Item>
          <Form.Item wrapperCol={{ offset: 6, span: 16 }}>
            <Space>
              <Button type="primary" htmlType="submit">
                登录
              </Button>
              <Link to={REGISTER_PATHNAME}>注册新用户</Link>
            </Space>
          </Form.Item>
        </Form>
      </div>
    </div>
  )
}

export default Login

在这段代码中,useDispatch 的作用是:
1. 获取 Redux store 的 dispatch 函数
2. 当登录成功后,主动调用 dispatch(loginReducer({ username, nickname })) 将用户信息存储到 Redux store 中
3. 这样在页面跳转后,目标页面就能立即从 Redux store 中获取到用户信息,而不需要等待异步请求
为什么需要这样做?
在我们之前的实现中,虽然登录成功并存储了 token,但在跳转到新页面时,目标页面需要通过 useLoadUserData 钩子来加载用户信息。这个钩子会检查 Redux store 中是否已经有用户信息:

// 判断当前 redux store 是否已经存在用户信息
const { username } = useGetUserInfo() // redux store
useEffect(() => {
  if (username) {
    setWaitingUserData(false) // 如果 redux store 已经存在用户信息,就不用重新加载了
    return
  }
  run() // 如果 redux store 中没有用户信息,则进行加载
}, [username])

如果我们不在登录成功后主动将用户信息存入 Redux,那么在跳转后,username 会是空的,导致 useLoadUserData 钩子发起 AJAX 请求去获取用户信息。由于浏览器的异步特性和可能的时序问题,这可能导致页面显示异常或者延迟。
通过使用 useDispatch 主动 dispatch 用户信息,我们确保了在跳转后,Redux store 中已经有正确的用户信息,避免了额外的网络请求和潜在的时序问题。

总结

useDispatch 是 React-Redux 中的关键 Hook,它使得函数组件能够与 Redux store 进行交互,通过 dispatching actions 来更新全局状态。在我们的案例中,它帮助我们解决了登录后跳转时的用户体验问题,确保用户信息能及时同步到 Redux store 中。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐