在我审查的一个拉取请求中,我注意到在许多拉取请求中看到的一种模式。React 组件具有多个 UI 状态,例如 loading
、error
和 success
。
作者使用了多个 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>
)
}
这些状态彼此不同。当 loading
为 true
时,error
和 success
应该为 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
挂钩 来设置 loading
、error
和 success
状态:
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 状态代码显示用户是否已解锁该功能。通常情况下,开发人员可能会使用 useState
和 useEffect
来管理该状态,这可能会增加不必要的复杂性:
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>
}
}
这种方法可以避免使用 useState
和 useEffect
进行额外的状态管理。
在编写 React 组件时,请务必考虑是否有必要使用 useState
和 useEffect
。通常情况下,它们是不必要的。