本篇笔记跟随官方教程而写,把其中连续的步骤碎片化,方便以后忘记的时候直接调用, 其中包含了对其他小问题的拓展。
创建路由(quick-start)
npm install react-router-dom
在React项目中使用,一般在最上层页面中配置,比如index.js或者App.js, etc
import {
createBrowserRouter,
ProviderRouter,
Route
}
...
const render = createBrowserRouter([
{
//index: true --to set as the default page
path: '/',
element: <APage/>,
errorElement: <ErrPage/>,
children: {
...
}
}
])
ReactDOM.createRoot(document.querySelector('#root')).render(
<React.StrictMode>
<ProviderRouter router={router}/>
</React.StrictMode>
)
使用loader和action
|- src
|- Person.js
|- Main.js
|- index.js
以一个根据数据动态加载页面为例,假设我们有以下数据
[
{
id: 2K39pAxd,
name: 'tom',
career: 'doctor'
},
...
]
//Person.js
...
export async function getData() {
const data = await localforage.getItem('data');
return data ? data : [] ;
}
export async function createDate() {
const data = await getData();
let item = generateData();//function that you use to generate a data item
data.unshift(item);
localforage.setIitem('data', data);
return data;
}
我们在上面的js中定义一系列的数据和API,接下来就需要将其应用到路由中去
我们需要根据上面的数据生成一个person page。
这里用到loader和action,先看是怎么应用的再进行说明。
//Main.js
import { getData, createData } from './Person'
export async function loader( {request, params} ) {
const Data = await getData();
return { Data };
}
export async function action( {request, params} ) {
await createData();
}
之后配置到路由中
(在上面的loader和action中,有两个参数,一个是request,就是form表单发送的产生的request对象,详细方法和属性看这里,params即路由中携带的参数)
import {loader, action} from './src/Main'
...
const router = createBrowserRouter([
{
path: '/',
element: <Main />,
loader: loader,
action: action
children: {
page: '/pages/:personId',
element: <personPage />
}
}
])
ok,从这里我们还没有看Main组件中受到loader的数据是怎么获取的,以及action要怎么触发,什么时候触发。接下来就分开进行说明
loader数据获取
在之前的coding中,通过loader,在Main组件挂载的时候(函数式组件),就向其传输了一块数据。react-router-dom提供了一个hook来获取这个数据
const data = useLoaderData();
action的机制
在上面的代码中,我们向路由配置了action,也编写了其实现函数,但唯独没有提到action的调用。
action在路由收到一个除Get外的提交时触发。
在传统的html中使用<form>
来构建一个表格,然后表格提交的时候,会根据其内容提交一次http请求给服务器,但这里是router,显然不需要这样的结果。在之前的学习中,一般使用e.preventDefault()
来进行处理,不过react-router-dom提供了另一个组件<Form>
来解决这个问题
//Main.js
...
<Form method='post'>
<button type='submit'>trigger the action</button>
</Form>
(但form中没有submit的input时,会默认以一个button作为提交)
在官方文档中,还提到了一个hook叫useNavigation
, 这应该是新版本的特性,因为在bing上搜索都没有对它的讨论。
import { useNavigation } from "react-router-dom";
function SomeComponent() {
const navigation = useNavigation();
navigation.state;
navigation.location;
navigation.formData;
navigation.formAction;
navigation.formMethod;
}
在页面发生跳转,比如点击一个<Link/>
或者提交发送一个表单数据,通过这个hook就可以对数据进行对应的处理。
比如
// Is this just a normal load?
let isNormalLoad =
navigation.state === "loading" &&
navigation.formData == null;
// Are we reloading after an action?
let isReloading =
navigation.state === "loading" &&
navigation.formData != null &&
navigation.formAction === navigation.location.pathname;
// Are we redirecting after an action?
let isRedirecting =
navigation.state === "loading" &&
navigation.formData != null &&
navigation.formAction !== navigation.location.pathname;
重定向到一个页面
在react-route-dom中,函数中重定向有下面几种方法(笔者目前所知道)
- 在
action
结束后进行跳转
async function action() {
return redirect('/');
}
useSunmit
import { useSubmit } from 'react-router-dom';
...
const submit = useSubmit();
submit(data, {
method:
action:
replace:
})
...
官方给出了一个用户超时登出的例子
import { useSubmit, useLocation } from "react-router-dom";
import { useEffect } from "react";
function AdminPage() {
useSessionTimeout();
return <div>{/* ... */}</div>;
}
function useSessionTimeout() {
const submit = useSubmit();
const location = useLocation();
useEffect(() => {
const timer = setTimeout(() => {
submit(null, { method: "post", action: "/logout" });
}, 5 * 60_000);
return () => clearTimeout(timer);
}, [submit, location]);
}
useNavigate
详细请参考useNavigate
import {useNavigate} from 'react-route-dom';
function APage() {
const navigate = useNavigate();
return (
<>
<button onClick={() => {
navigate(-1);
}}>return to last page</button>
</>
);
}
useFetcher
doc
首先介绍一个概念:
Optimistic UI is a pattern that you can use to simulate the results of a mutation and update the UI even before receiving a response from the server
也就是一种在服务端返回之前就得到对应结果。
useFecher()
返回这样一个对象:
import { useFetcher } from "react-router-dom";
function SomeComponent() {
const fetcher = useFetcher();
// call submit or load in a useEffect
React.useEffect(() => {
fetcher.submit(data, options);
fetcher.load(href);
}, [fetcher]);
// build your UI with these properties
fetcher.state;
fetcher.formData;
fetcher.formMethod;
fetcher.formAction;
fetcher.data;
// render a form that doesn't cause navigation
return <fetcher.Form />;
}
看起来和useNavigation
的结果一样。但是区别在于,useFetch
会绕过navigate
,直接触发对应的loader
和action
。
所以通过fetcher.formData
,可以在不触发navigate
的情况下就可以更新数据,UI,以及其他的操作。