在React中,你可能真的不需要useState?

前有科技后进阶 2024-04-28 05:34:38

大家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

React 组件通常具有多种 UI 状态,例如:加载中 (loading)、错误 (error) 和成功 (success)。 通常,开发者会使用多个 useState Hooks 来管理状态,但是这通常会导致代码难以阅读且极易出错,例如:

const MyComponent = () => { const [loading, setLoading] = useState(false) const [error, setError] = useState(false) const [success, setSuccess] = useState(false) // 使用多个 useState Hooks 管理状态 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 Hooks 可能会导致意外行为,例如:同时将两个状态设置为 true。

此时开发者可以考虑使用 “有限状态机”(Finite State Machine,即 FSM) 模式,而 FSM 只允许有限数量的状态非常契合上面的场景。 在上面的 UI 示例中,单个 useState 可以更稳健地管理当前状态,并且出错的风险更低,如下所示:

import {useState} from 'react'type State = 'loading' | 'error' | 'success'const MyComponent = () => { const [state, setState] = useState<State>('loading') const handleClick = () => { setState('loading') // 模拟异步操作 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 query(简化 Web 应用程序中的获取、缓存、同步和更新服务器状态)来获取数据时,useQuery 无需使用单独的 useState Hooks 来处理 loading, error 和 success 等多种状态:

import { useQuery,} from '@tanstack/react-query'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>}

以上示例中,假如开发者还需要考虑另一种称为 “锁定” 的状态,其根据来自服务器的 403 状态指示用户是否已解锁该功能。通常开发者可能会使用 useState 和 useEffect 来管理状态,但是缺点就是会增加不必要的复杂性:

const MyComponent = () => { const [locked, setLocked] = useState(false) // 使用 useState Hooks 管理新的状态 const {data, isLoading, error} = useQuery(...) useEffect(() => { if (error && error.status === 403) { setLocked(true) } }, [error]) if (locked) { return <p>You are locked out</p> }}

然而,实际上更好的方法是直接从 error 状态中派生出 locked 状态,比如下面的例子:

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。正如上面的示例,很多 useState、useEffect 都能够被简化掉。

参考资料

本篇文章部分内容来自 Nico Prananta 发表的文章《You don't need useState in React》

https://www.nico.fyi/blog/you-dont-need-usestate-in-react

https://www.telerik.com/blogs/how-to-use-finite-state-machines-react

https://tanstack.com/query/latest/docs/framework/react/overview

https://github.com/tanstack/query

https://www.youtube.com/watch?v=h9hYnDe8DtI

https://www.youtube.com/watch?v=gTl5x_LQd_s

0 阅读:0

前有科技后进阶

简介:感谢大家的关注