likes
comments
collection
share

再见了useState和useEffect: useLoaderData彻底改变React开发

作者站长头像
站长
· 阅读数 15

原文地址 再见了useState和useEffect:彻底改变React开发! 2023.5.16, by Emmanuel Odii

再见了useState和useEffect: useLoaderData彻底改变React开发

许多开发人员继续使用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传入你创建的加载器函数。

下面的代码片段清楚地说明这个createBrowserRoutercreateRouteFromElementsloaderLoaderFunction

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当一个纯粹的路由来使用。

转载自:https://juejin.cn/post/7250355200510558263
评论
请登录