React的 useState钩子是在React组件的上下文中持久化状态的一个好方法。这篇文章演示了一个简单的React钩子,它将状态存储在URL查询字符串中,建立在React RoutersuseSearchParams 钩子之上。
useState
useState 钩子的使用看起来像这样:
const [greeting, setGreeting] = useState('hello world');
// ....
setTotal('hello John'); // will set greeting to 'hello John'
然而,使用useState 有一个缺点;该状态是不持久的,不能共享。因此,如果你想让别人看到你在一个应用程序中能看到的东西,你就得依赖他们执行让你的应用程序进入当前状态的相同操作。
这样做既费时又容易出错,所以如果有一个简单的方法来分享状态,那不是很好吗?
一个有状态的URL
在用户之间共享状态的一个有效方法是使用URL,而不需要一个持久化的后台。一个URL可以包含所需的状态,其形式是路由和查询链/搜索参数。搜索参数特别强大,因为它们完全是通用的和可定制的。
多亏了URLSearchParams API,我们可以在不往返服务器的情况下操作查询参数--这是我们可以建立的一个基础;只要不超过URL的限制(大约2000个字符),我们就可以自由地在你的URL中持久化状态。
useSearchParams
如果你正在使用React,React Router项目使消耗URL中的状态,特别是以querystring或搜索参数的形式,很简单。它通过 useSearchParams钩子:
import { useSearchParams } from "react-router-dom";
const [searchParams, setSearchParams] = useSearchParams();
const greeting = searchParams.get('greeting');
// ...
setSearchParams({ 'greeting': 'bonjour' }); // will set URL like so https://our-app.com?greeting=bonjour - this value will feed through to anything driven by the URL
这是一个很好的机制,可以在本地和以可共享的方式持久保存状态。
这种方法的一个重要好处是,它不需要发布到服务器上。它只是使用浏览器的API,如URLSearchParams API。改变一个查询字符串参数完全是在本地即时发生的。
useSearchParamsState 钩子
useSearchParams 钩子不做的是维护其他查询字符串或搜索参数。
如果你在你的应用程序中维护多个状态,这可能意味着多个查询字符串或搜索参数。那么,一个允许我们更新状态而不丢失其他状态的钩子将是非常有用的。
此外,如果我们不需要先获取searchParams 对象,然后再对其进行操作,那就更好了。现在是我们的useSearchParamsState 钩子的时候了:
import { useSearchParams } from "react-router-dom";
export function useSearchParamsState(
searchParamName: string,
defaultValue: string
): readonly [
searchParamsState: string,
setSearchParamsState: (newState: string) => void
] {
const [searchParams, setSearchParams] = useSearchParams();
const acquiredSearchParam = searchParams.get(searchParamName);
const searchParamsState = acquiredSearchParam ?? defaultValue;
const setSearchParamsState = (newState: string) => {
const next = Object.assign(
{},
[...searchParams.entries()].reduce(
(o, [key, value]) => ({ ...o, [key]: value }),
{}
),
{ [searchParamName]: newState }
);
setSearchParams(next);
};
return [searchParamsState, setSearchParamsState];
}
上面的钩子可以大致被认为是useState<string> ;但是将该状态存储在URL中。
让我们思考一下它是如何工作的。当初始化时,该钩子需要两个参数:
searchParamName:这是保存状态的querystring参数的名称。defaultValue:如果querystring中没有值,这就是后备值。
然后该钩子继续包裹useSearchParams 钩子。它询问searchParams 中提供的searchParamName ,如果它不存在,就返回到defaultValue 。
setSearchParamsState 方法的定义看起来有点复杂,但基本上它所做的就是获取现有搜索参数的内容,并为当前属性应用新的状态。
也许值得在这里停顿一下,观察一下潜伏在这个实现中的一个观点。实际上,为同一个搜索参数设置多个值是有效的。虽然这是有可能的,但这在某种程度上是很少被使用的--这个实现只允许任何给定的参数有一个单一的值,因为这是相当有用的行为。
有了这一切,我们就有了一个可以这样使用的钩子:
const [greeting, setGreeting] = useSearchParamsState("greeting", "hello");
上面的代码返回了一个greeting ,这个值来自于greeting 搜索参数。它还返回一个setGreeting 函数,允许我们设置greeting 值。这和useState 的API是一样的,所以对于React的用户来说,应该感到很习惯。巨大的!
在你的网站上持久化查询系统
现在,我们有了这个令人兴奋的机制,它允许我们在我们的URL中存储状态,并且,作为一个结果,通过向别人发送我们的URL,很容易分享状态。
同样有用的是一种在我们的网站上导航而不丢失状态的方法。想象一下,我选择了一个日期范围并存储在我的URL中。当我从一个屏幕点击到另一个屏幕时,我希望能保持这个状态。我不想在每个屏幕上都要重新选择日期范围。
我们怎样才能做到这一点呢?嗯,事实证明这很容易。我们所需要的是useLocation 钩子和相应的location.search 属性。这代表了查询字符串,因此,每次我们渲染一个链接时,我们只需像这样包含它:
class="ts language-ts">const [location] = useLocation();
return (<Link to={`/my-page${location.search}`}>Page</>)
现在,当我们在我们的网站上导航时,这个状态将被保持。
总结
在这篇文章中,我们创建了一个useSearchParamsState 钩子,它允许状态被持久化到URL中,以达到共享的目的。