你不需要总是在 React 中使用 useState

在我审查的一个拉取请求中,我注意到在许多拉取请求中看到的一种模式。React 组件具有多个 UI 状态,例如 loadingerrorsuccess

作者使用了多个 useState 钩子来管理这些状态,这导致代码难以阅读且容易出错,例如:

js 复制代码
const MyComponent = () => {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(false)
  const [success, setSuccess] = useState(false)

  return (
    <div>
      {loading && !error && !success && <p>Loading...</p>}
      {error && !loading && !success && <p>Error occurred</p>}
      {success && !loading && !error && <p>Operation completed successfully</p>}
    </div>
  )
}

这些状态彼此不同。当 loadingtrue 时,errorsuccess 应该为 false。使用多个 useState 钩子可能会导致意外行为,例如意外 true 同时设置两个状态。

相反,请考虑使用有限状态机(FSM) 模式。FSM 只允许有限数量的状态。在上面的 UI 示例中,单个 useState 可以更稳健地管理当前状态,并且出错的风险更低,如下所示:

js 复制代码
import { useState } from 'react'

type State = 'loading' | 'error' | 'success'

const MyComponent = () => {
  const [state, setState] = useState<State>('loading')

  const handleClick = () => {
    setState('loading')
    // Simulate an async operation
    setTimeout(() => {
      setState('success')
    }, 2000)
  }

  return (
    <div>
      {state === 'loading' && <p>Loading...</p>}
      {state === 'error' && <p>Error occurred</p>}
      {state === 'success' && <p>Operation completed successfully</p>}
      <button onClick={handleClick}>Click me</button>
    </div>
  )
}

在某些情况下,例如使用 Tanstack 查询来获取数据时,useQuery 无需单独 useState 挂钩 来设置 loadingerrorsuccess 状态:

js 复制代码
const MyComponent = () => {
  const { data, isLoading, error } = useQuery(...)

  if (isLoading) {
    return <p>Loading...</p>
  }

  if (error) {
    return <p>Error occurred</p>
  }

  return <p>Operation completed successfully {data}</p>
}

让我们考虑另一个名为 locked 的状态,它根据服务器发送的 403 状态代码显示用户是否已解锁该功能。通常情况下,开发人员可能会使用 useStateuseEffect 来管理该状态,这可能会增加不必要的复杂性:

js 复制代码
const MyComponent = () => {
  const [locked, setLocked] = useState(false)
  const { data, isLoading, error } = useQuery(...)

  useEffect(() => {
    if (error && error.status === 403) {
      setLocked(true)
    }
  }, [error])

  if (locked) {
    return <p>You are locked out</p>
  }
}

更好的方法是直接从 error 中推导出锁定状态:

js 复制代码
const MyComponent = () => {
  const { data, isLoading, error } = useQuery(...)

  if (isLoading) {
    return <p>Loading...</p>
  }

  const locked = error?.status === 403

  if (locked) {
    return <p>You are locked out</p>
  }
}

这种方法可以避免使用 useStateuseEffect 进行额外的状态管理。

在编写 React 组件时,请务必考虑是否有必要使用 useStateuseEffect。通常情况下,它们是不必要的。

相关推荐
漂流瓶jz5 小时前
Webpack如何实现万物皆可import?loader的使用/配置/手写实践
前端·javascript·webpack
ZC跨境爬虫5 小时前
跟着 MDN 学CSS day_41:显式轨道、隐式网格与区域命名放置
前端·javascript·css·ui·交互
weelinking6 小时前
【产品】12_接入数据库——让数据永久保存
jvm·数据库·python·react.js·数据挖掘·前端框架·产品经理
修己xj6 小时前
告别手动存图!这款叫 Fatkun 的浏览器插件,简直是素材收集神器
前端
袋鼠云数栈7 小时前
从前端到基础设施,ACOS 如何打通企业全链路可观测
运维·前端·人工智能·数据治理·数据智能
AskHarries7 小时前
系统提示词、开发者指令和用户输入的优先级
java·前端·数据库
Moment7 小时前
长上下文会最终杀死 Rag 吗?
前端·javascript·后端
qcx238 小时前
【系统学AI】25 论文导读 ①:两篇改变 AI 的开山之作——Attention Is All You Need & ReAct
前端·人工智能·react.js·transformer
kyriewen9 小时前
大文件上传最全指南:分片、断点续传、秒传,一篇就够了
前端·javascript·面试
我叫黑大帅9 小时前
解决聊天页内部滚轮改为页面滚动问题
javascript·后端·面试