React中常用的hook函数(四)——useRef、useNavigate、useLocation和useSearchParams
一、useRef
1. 基础概念:
useRef
返回一个可变的 ref
对象,这个对象的 .current
属性可以用来存储一个值,该值在组件的生命周期内是持久化的,并且它不会导致组件重新渲染。
语法:
const myRef = useRef(initialValue);
initialValue
:这是useRef
的初始值,通常是null
或者某个值。myRef
:这是返回的一个对象,具有.current
属性,存储着你希望持久化的数据。
2. 用法 1:获取和操作 DOM 元素
比如,你可能需要在某个操作发生时聚焦一个输入框,或者读取一个 DOM 元素的尺寸信息等。
示例:访问 DOM 元素
import React, { useRef, useEffect } from 'react';
const FocusInput = () => {
const inputRef = useRef(null);
useEffect(() => {
// 组件加载完成后,自动聚焦到输入框
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" />
</div>
);
};
声明一个ref对象,通过input的ref属性将ref对象绑定,此时inputRef的current属性会指向该输入框的 DOM 元素
3. 用法 2:保持对数据的引用(不会触发重新渲染)
除了访问 DOM 元素,useRef
还可以用来存储其他可变的数据(例如计时器 ID、上一个值等),并且它不会引起组件重新渲染。
示例:持久化某个值
import React, { useState, useRef, useEffect } from 'react';
const TimerComponent = () => {
const [seconds, setSeconds] = useState(0);
const timerRef = useRef(null);
useEffect(() => {
timerRef.current = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// 清理计时器
return () => clearInterval(timerRef.current);
}, []);
return (
<div>
<h1>{seconds} seconds</h1>
</div>
);
};
- 在这个例子中,
timerRef
用来存储计时器的 ID(一个可变的值)。即使seconds
状态发生变化,timerRef
的值不会改变,它仅用来引用计时器。 useRef
允许我们避免因为timerRef
的更新而导致不必要的组件重新渲染。
4. 用法 3:访问和持久化函数中的状态
有时你可能需要访问某个值的上一次状态。useRef
也能帮助我们存储一个值,在每次渲染时持久化下来,而不会因为它的改变导致重新渲染。
示例:记录上一个状态值
import React, { useState, useEffect, useRef } from 'react';
const PreviousState = () => {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]); // 每次 `count` 变化时,更新 `prevCountRef`
return (
<div>
<h1>Current: {count}</h1>
<h2>Previous: {prevCountRef.current}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
- 在这个例子中,
prevCountRef
用来存储count
的上一个值。每次count
更新时,prevCountRef.current
会持有之前的值。 - 由于
useRef
的特性,prevCountRef
的改变不会导致组件重新渲染,所以它不会影响性能。
5. useRef
与 useState
的区别
useState
:用于存储需要在 UI 上表现出来的状态。每当你调用setState
更新状态时,React 会重新渲染组件。useRef
:用于存储不需要触发重新渲染的状态。它的值是持久化的,不会随着组件的重新渲染而改变。
总结
useRef
的.current
属性在组件的整个生命周期内都保持不变。因此,您可以将它作为持久化存储来引用外部资源、DOM 元素或者数据,而不必担心它会导致组件重新渲染。- 如果你将
useRef
用作数据存储时,不要把它当作普通的 React 状态来使用。因为如果你直接改变useRef
的值(例如ref.current = newValue
),这不会触发渲染更新,因此当你需要依赖于 UI 更新时,应该使用useState
或者useReducer
。
二、useNavigate
useNavigate
是 React Router v6 中的一个 Hook,用于在函数组件中进行编程式的导航。通过 useNavigate
,你可以在没有用户直接点击链接的情况下进行路由跳转。它通常与事件处理函数或条件逻辑一起使用。
1. 基础概念:
useNavigate
返回一个函数,允许你在代码中进行路由跳转。该函数接受一个路径(可以是字符串)或一个包含导航选项的对象,并将页面重定向到相应的路由。
语法:
const navigate = useNavigate();
然后,你可以使用 navigate
来触发导航:
navigate('/new-path'); // 跳转到指定路径
navigate(-1); // 跳转到上一个历史记录(类似浏览器的返回按钮)
navigate('/new-path', { replace: true }); // 跳转,并替换当前的历史记录
2. 常见用法:
示例 1:基本导航
import React from 'react';
import { useNavigate } from 'react-router-dom';
const HomePage = () => {
const navigate = useNavigate();
const goToAbout = () => {
navigate('/about'); // 编程式跳转到“/about”路径
};
return <button onClick={goToAbout}>Go to About</button>;
};
示例 2:带参数的导航
import React from 'react';
import { useNavigate } from 'react-router-dom';
const UserProfile = () => {
const navigate = useNavigate();
const goToProfile = (userId) => {
navigate(`/profile/${userId}`); // 跳转到具体用户的个人页面
};
return <button onClick={() => goToProfile(123)}>Go to User 123's Profile</button>;
};
此例演示了如何将动态路径作为参数传递给 navigate
,从而跳转到用户特定的页面。
3. 导航选项:
navigate
还可以接收一个对象作为第二个参数,允许你设置一些额外的导航选项,比如是否替换历史记录,或者传递状态数据。
示例 3:使用导航选项
import React from 'react';
import { useNavigate } from 'react-router-dom';
const LoginPage = () => {
const navigate = useNavigate();
const handleLogin = () => {
// 登录成功后跳转到首页,并替换当前历史记录
navigate('/', { replace: true });
};
return <button onClick={handleLogin}>Login</button>;
};
replace
:如果设置为true
,则会替换当前历史记录,避免用户通过浏览器的“后退”按钮返回到此页面。state
:你还可以通过state
传递一些状态数据,在导航后可以通过location.state
访问这些数据。
navigate('/dashboard', { state: { from: 'login' } });
4. 后退和前进:
navigate
支持通过数字来实现类似浏览器历史记录的前进和后退操作。
示例 4:模拟浏览器的后退和前进
import React from 'react';
import { useNavigate } from 'react-router-dom';
const NavigationButtons = () => {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>Back</button> {/* 后退 */}
<button onClick={() => navigate(1)}>Forward</button> {/* 前进 */}
</div>
);
};
navigate(-1)
:后退一页,类似浏览器的“后退”按钮。navigate(1)
:前进一页,类似浏览器的“前进”按钮。
5. 与其他 React Router 组件的结合使用:
useNavigate
可以与其他 React Router 组件(如 Route
、Link
等)一起使用,来实现更复杂的导航逻辑。
示例 5:结合条件渲染导航
import React from 'react';
import { useNavigate } from 'react-router-dom';
const ProtectedPage = ({ isAuthenticated }) => {
const navigate = useNavigate();
if (!isAuthenticated) {
// 如果没有认证,重定向到登录页面
navigate('/login', { replace: true });
}
return <div>Protected Content</div>;
};
在这个例子中,如果用户未认证,将会在渲染时进行跳转。
6. useNavigate
与 useHistory
的区别
useHistory
:在 React Router v5 中使用,提供一个历史记录对象,用来进行导航。useNavigate
:在 React Router v6 中替代了useHistory
,提供了更加简洁的 API(没有历史对象,直接返回一个navigate
函数)。
7. 总结:
useNavigate
是 React Router v6 提供的用于编程式导航的 Hook,它简化了路由跳转的方式。- 使用
navigate
函数,你可以轻松地进行路径跳转、替换历史记录、或者实现类似浏览器“后退”与“前进”的功能。useNavigate
提供了更多灵活的导航选项,可以使路由管理更加精细和高效。
三、useLocation
useLocation
是 React Router 提供的一个钩子(hook),用于访问当前的浏览器位置对象。这可以让你获取当前的 URL 路径、查询参数、哈希值等信息,从而在函数组件中实现基于路由的动态渲染。
主要特性:
-
返回位置对象:
useLocation
返回一个位置对象 (location
),包含以下几个主要属性:- pathname: 当前 URL 的路径部分(例如:
/home
)。 - search: 查询字符串(例如:
?id=123&name=abc
)。 - hash: 哈希值(例如:
#section1
)。 - state: 传递的状态对象,通常用于在页面间传递一些非 URL 参数的数据。
- pathname: 当前 URL 的路径部分(例如:
-
用途:
- 在 React 应用中动态地根据当前路径或查询参数更新 UI。
- 实现路由相关的逻辑,比如基于 URL 路径变化显示不同的内容。
-
示例:
import { useLocation } from 'react-router-dom'; function MyComponent() { const location = useLocation(); return ( <div> <p>Current Path: {location.pathname}</p> <p>Query String: {location.search}</p> <p>Hash: {location.hash}</p> </div> ); }
-
动态更新:
useLocation
会随着浏览器的 URL 变化自动更新。如果你想监听路由变化,可以依赖location
对象的变化来触发重新渲染。 -
与
useHistory
比较:useHistory
主要用于程序化导航,通过访问历史对象来控制跳转。useLocation
关注的是获取当前的位置信息,通常用于展示和路由状态的读取。
使用场景:
- 根据 URL 查询参数改变页面内容。
- 在某些页面上根据路径进行特定的 UI 展示(例如,导航栏高亮显示)。
- 处理哈希路由或者与后端交互时,根据不同路径做动态数据加载。
四、useSearchParams
useSearchParams
是 React Router v6 中提供的一个钩子(hook),用于读取和修改 URL 中的查询参数(即 ?key=value
的部分)。与传统的 window.location.search
不同,useSearchParams
提供了更方便的方式来处理查询字符串,同时支持 React Router 的状态管理和自动更新。
主要特性:
1.返回值: useSearchParams
返回一个数组,包含两个元素:
searchParams
: 这是一个URLSearchParams
对象,允许你读取、修改查询参数。setSearchParams
: 这是一个函数,允许你更新查询参数并触发 URL 更新。
2.URLSearchParams
对象:
searchParams
是一个 URLSearchParams
实例,提供了多种方法来操作查询字符串
get(name)
: 获取某个查询参数的值(例如:searchParams.get('id')
)。getAll(name)
: 获取某个查询参数的所有值(对于重复参数,返回一个数组)。set(name, value)
: 设置某个查询参数的值。has(name)
: 检查查询参数是否存在。delete(name)
: 删除某个查询参数。
3.示例代码:
import { useSearchParams } from 'react-router-dom';
function MyComponent() {
// 获取当前的查询参数
const [searchParams, setSearchParams] = useSearchParams();
// 获取特定查询参数值
const id = searchParams.get('id');
const name = searchParams.get('name');
// 修改查询参数
const updateParams = () => {
setSearchParams({ id: '456', name: 'John' });
};
return (
<div>
<p>Current ID: {id}</p>
<p>Current Name: {name}</p>
<button onClick={updateParams}>Update Params</button>
</div>
);
}
4.动态更新: useSearchParams
会随着查询参数的变化自动更新组件。如果查询参数发生变化,React 会重新渲染组件。你可以使用 searchParams
来读取当前查询字符串,使用 setSearchParams
来更新它。
5.更新查询参数: 当调用 setSearchParams
时,查询参数会被更新并触发页面的重新渲染。可以传递一个新的对象(例如 { id: '456', name: 'John' }
)来替换当前的查询参数,也可以传递一个函数来基于当前的查询参数进行更新。
setSearchParams(prev => {
// 保留现有参数,修改某个字段
prev.set('id', '456');
return prev;
});
使用场景:
- 控制分页和过滤器: 你可以利用查询参数(如
page
、filter
)来控制内容的分页或筛选。- 分享链接: 在需要共享带有查询参数的链接时,
useSearchParams
允许你动态地更改查询参数并更新 URL。- 维持 URL 状态: 在多个视图间保持查询参数一致,如在搜索功能中更新查询参数,以便用户刷新页面时仍然能看到相同的结果。