原文地址 再见了useState和useEffect:彻底改变React开发! 2023.5.16, by Emmanuel Odii
许多开发人员继续使用useState和useEffect Hook来更新状态,但是我不喜欢这种访问。问题是,它会导致组件同时挂载、重新挂载和卸载,从而导致意外行为。因此,在控制台输出日志时,你可能会看到结果重复三次。
介绍 useLoaderData Hook
hook是React中的一个自定义hook,可以帮助你将数据加载到组件中。它简化了从API获取数据或执行任何异步操作的过程
当你使用useLoaderData钩子时,你需要为它提供一个返回Promise的函数。这个Promise表示一个获取你需要的数据的异步操作。一旦Promise resolve,数据就可以被组件访问了。
hook为你处理加载状态,因此你不需要手动跟踪数据是否仍在加载或是否已完成加载。它提供了一种方便的方式来访问数据,还可以处理数据加载过程中可能发生的任何潜在错误
通过使用hook,你可以保持组件代码的整洁和有组织,将数据加载逻辑与组件的其他职责分离。它允许你以一种对初学者更友好的方式轻松获取和管理数据
为什么使用useLoaderHook?
react-router中的useLoaderHook帮助我们以最少的工作量实现相同的功能。这些是你应该使用它的一些例子。
- 加载状态管理:加载器为你处理加载状态,提供数据何时获取的明确指示。这有助于管理加载微调框、进度指示器或与数据加载相关的任何其他UI元素。
- 错误处理:加载器通常包含错误处理机制,允许你处理和显示数据加载过程中发生的错误。它们提供了一种标准的错误处理方式,使得在应用程序中实现一致的错误处理变得更容易。
- 关注点分离:加载器允许你将数据加载逻辑与组件的其他方面分离。这可以促进更好的代码组织和可维护性,因为你可以专注于特定的职责而不会混淆它们。
还有更多。
让我们看看这是如何工作的。
假设你对react-router6的工作原理有很好的了解。如果你不知道,请随时查看这里的文档
首先,我们必须在我们的应用程序中设置路由系统以与Loader API一起工作。在此之前,我们一直在使用BrowserRouter设置来处理我们应用程序的各种路由。
我们花点时间讨论一下。
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom"
import HomeComponent from "./home"
import AboutCompoent from "./about"
function App () {
<BrowserRouter>
<Routes>
<Route path='/' element={<Outlet />}>
<Route index element={<HomeComponent /> } />
<Route path='about' element={<AboutComponent/> } />
</Route>
</Routes>
</BrowserRouter>
};
export default App;
在这里,我们已经使用传统的方法从react-router中导入的内容建立了一个路由系统。
思考一下发生了什么。
是的。react-router中的BrowserRouter创建了一个children对象的数组。下面的代码片段清楚地说明了这是如何工作的Routes
BrowserRouter([
{
path: '/',
element: <HomeComponent />,
children: []
},
{
path: '/about',
element: <AboutComponent/>,
children: []
}
])
如果它们是嵌套路由,那么它会将子路由附加到父路由中子路由的键上。
是的,它就是这样递归的。
然而,此方法不能用于使用loaderData钩子。我们需要做一点重构。别急,我知道你很急,但是你先别急。和这个有点像。我强烈建议你查看react-router文档以获取更多信息。
import {
createBrowserRouter,
createRoutesFromElements,
RouterProvider,
Route,
Outlet
} from "react-router-dom"
import HomeComponent from "./home"
import AboutComponent from "./about"
function App() {
const browserRoutes = createBrowserRouter(createRoutesFromElements(
<Route path='/' element={<Outlet />}>
<Route index element={<HomeComponent /> } />
<Route path='about' element={<AboutComponent /> } />
</Route>
))
return (
<RouterProvider router={browserRoutes} />
);
}
我已经导入了一些对象。
然后,初始化一个变量,作为要渲染的对象。注意到我在函数内部调用了函数。这是因为我想将路由解析或转换为对象,正如其名称所示,可以帮助我实现这一点。最后返回一个新的。让我们看看如果不使用createRoutesFromElements函数,我们会做什么。createBrowserRouter``createRoutesFromElement``RouterProvider``browserRoutes``createRoutesFromElements``createBrowserRouter``createRoutesFromElements``RouterProvider``browserRouter
createBrowserRouter([
{
path: '/',
element: <HomeComponent />,
children: []
},
{
path: '/about',
element: <AboutComponent/>,
children: []
}
])
我对这种方式不是很感兴趣,因为你的路由甚至可以嵌套,在某些时候,这会变得令人困惑。你应该让事情变得非常简单。
探索加载器函数:
现在我们对如何设置应用程序以使用Loader API有了一些了解,让我们看看如何使用API。
假设你想从某个端点获取数据并将其显示在组件中。大多数开发人员会做的是:初始化一个state,然后在useEffect钩子中更新状态。下面的代码片段清楚地说明了我所说的。homecomponent
import { useState } from 'react'
const HomeComponent = () => {
const [data, setData] = useState([]);
useEffect(async () => {
const request = await fetch('http://localhost:3004/file');
if(!request.ok) throw new Error('Failed to fetch data')
const item= await request.json()
setData(item)
}, [])
return (
<section>
{ data.length > 0 ? data.map((foundData) => (
<div key={foundData.id}>
<strong>{foundData.name}</strong>
</div>
)) : <p>Data currently unavailable</p>
}
</section>
)
}
export default HomeComponent
这有很多行,因为我们可能想简化一下,并重用相同的函数。
要使用加载器(Loaders),必须定义一个加载器函数。加载器函数类似于自定义钩子(Custom Hooks)。
此外,函数的命名约定并不重要,因为你可以给它取任何名字。在下面的代码片段中,我将创建一个基本的加载器函数,它从API中获取数据,就像上面代码片段中所示的那样
export async function LoaderFunction () {
const request = await fetch('http://localhost:3004/file');
if (!request.ok) throw new Error ('Failed to fetch item')
const item = await response.json();
return item;
};
现在,我们必须将加载器函数导入到正在处理路由的组件中。 使用设置路线系统后,您应该使用props属性loader传入你创建的加载器函数。
下面的代码片段清楚地说明这个createBrowserRouter
、createRouteFromElements
、loader
、LoaderFunction
import {
createBrowserRouter,
createRoutesFromElements,
RouterProvider,
Route,
Outlet
} from "react-router-dom"
import HomeComponent from "./home"
import AboutComponent from "./about"
import { LoaderFunction as HomeLoader} from "./loader"
function App() {
const browserRoutes = createBrowserRouter(createRoutesFromElements(
<Route path='/' element={<Outlet />}>
<Route index element={<HomeComponent /> }
loader={HomeLoader}/>
<Route path='about' element={<AboutComponent /> } />
</Route>
))
return (
<RouterProvider router={browserRoutes} />
);
}
之后,我们可以使用home组件中的react-router中的useLoaderData钩子访问loader函数返回的数据。
下面的代码片段最好地解释了刚刚阅读的内容。
import { useLoaderData } from "react-router-dom"
const HomeComponent = () => {
const data = useLoaderData();
return (
<section>
{data.map((foundData) => (
<div key={foundData.id}>
<strong>{foundData.name}</strong>
</div>
))}
</section>
)
}
export default HomeComponent
Wow! ?..
现在看看我们是如何清理home组件的:)
注意到我们去掉了检查数据是否为null的逻辑。
这是因为react-router会让它在url/路径激活时就加载数据。因此,它甚至在组件挂载之前
就发出了必要的请求。是的!
我们只是在为幸福之路做准备。如果我们传递一个不存在的url请求呢?如果是这种情况,不要惊慌,因为react-router也允许我们将组件传递给另一个被调用的prop参数errorElement
。
这是专门针对我们使用时发生错误的。让我们通过下面的代码片段中看看它是如何工作的。
import {
createBrowserRouter,
createRoutesFromElements,
RouterProvider,
Route,
Outlet
} from "react-router-dom"
import HomeComponent from "./home"
import AboutComponent from "./about"
import { LoaderFunction as HomeLoader} from "./loader"
function App() {
const browserRoutes = createBrowserRouter(createRoutesFromElements(
<Route path='/' element={<Outlet />}>
<Route
index
element={<HomeComponent />}
loader={HomeLoader}
errorElement={<h1>An Error occured</h1>}
/>
<Route path='about' element={<AboutComponent /> } />
</Route>
))
return (
<RouterProvider router={browserRoutes} />
);
}
我只是使用了一个h1
标签来显示错误。建议你使用一个组件,这样你就可以访问钩子。我将在我即将发布的一篇博客文章中展示如何使用useRouteError钩子。如果你想了解它,请使用这个链接。
由于它在挂载组件之前预取数据,加载状态变得无关紧要,因为它可能会获取数据或返回错误消息,你将其作为值传递给errorElement。
这就是使用Data Layer API(数据层API)发出请求所需要知道的所有基础知识。
如果你觉得这篇文章对你有帮助,请考虑在Twitter上关注我,回复这篇文章,留下评论,或者通过这个链接请我喝咖啡来支持我。
译者注:恭喜你看到这里,原作者的标题其实是过于标题党了。
我在学习react-router的时候,通过react-router官网的文档的联系人管理的例子学习了useLoaderData用法,useLoaderData需要把查询数据、更改数据的逻辑拆分出去,并注册到路由上,虽然降低了组件内的心智负担,但是增强了路由配置的心智负担。
因为有了hook,组件内的代码逻辑其实已经降低很多了,我个人更喜欢把逻辑放到组件中、哪怕拆分到hook中,也是非常便于理解和维护的。
而react-router的useLoaderdata
加载数据、使用react-router的Form
实现编辑数据,它更像一种模式,感觉react-router想做更多事情,其实不利于react-router的使用。
useLoaderdata
可以在一些业务简单的页面使用、比如公告、简单的表单里使用。
我更倾向于把react-router当一个纯粹的路由来使用。