[英]Best practice for Next.js data fetching inside a component
I have a menu component that appears globally.我有一个全局显示的菜单组件。 What is the best practice for getting data into that component?
将数据导入该组件的最佳做法是什么?
I'm trying to take advantage of static generation that Next.js offers but all data fetching guidance from the Next.js team relates to pages.我正在尝试利用 Next.js 提供的 static 代,但 Next.js 团队的所有数据获取指南都与页面相关。
getStaticProps
and getStaticPaths
seem to pertain to page generation, not data for components. getStaticProps
和getStaticPaths
似乎与页面生成有关,而不是组件数据。 Is their SWR
package the right answer, or Apollo Client?他们的
SWR
package 是正确答案,还是 Apollo Client?
Typically in hooks-based React, I'd just put my data call into useEffect
.通常在基于钩子的 React 中,我只是将我的数据调用放入
useEffect
中。 I'm not sure how to reason this out being that everything is rendered at build time with Next.我不确定如何推断出所有内容都是在构建时使用 Next 呈现的。
This is such a tricky problem, I think we need to lay out some background before a solution comes into focus.这是一个如此棘手的问题,我认为我们需要在解决方案成为焦点之前布置一些背景。 I'm focusing in the React.js world but a lot of this would apply to Vue/Nuxt I'd imagine.
我专注于 React.js 世界,但我认为其中很多都适用于 Vue/Nuxt。
Gatsby and Next are focused on generating static pages, which vastly improves performance and SEO in React.js sites. Gatsby 和 Next 专注于生成 static 个页面,这极大地提高了 React.js 站点的性能和 SEO。 There is a lot of technical overhead to both platforms beyond this simple insight but let's start with this idea of a digital machine pumping out fancy HTML pages for the browser.
除了这个简单的洞察力之外,这两个平台都有很多技术开销,但让我们从这个数字机器为浏览器抽出精美的 HTML 页面的想法开始。
In the case of Next.js (as of v9.5
), their data fetching mechanism getStaticProps
does most of the heavy lifting for you but it's sandboxed to the /pages/
directory.对于 Next.js(从
v9.5
开始),他们的数据获取机制getStaticProps
为您完成了大部分繁重的工作,但它被沙盒化到/pages/
目录。 The idea is that it does the data fetching for you and tells the Next.js page generator in Node about it during build time (instead of doing it component-side in a useEffect
hook - or componentDidMount
).这个想法是它为您获取数据并在构建期间将其告诉 Node 中的 Next.js 页面生成器(而不是在
useEffect
挂钩 - 或componentDidMount
中在组件端执行)。 Gatsby does much the same with their gatsby-node.js
file, which orchestrates the data fetching for page building in concert with a Node server. Gatsby 对他们的
gatsby-node.js
文件做了很多相同的事情,该文件与 Node 服务器协调页面构建的数据获取。
You can use both Gatsby and Next to produce any kind of website but a huge use case are CMS-driven websites, because so much of that content is static. These tools are an ideal fit to that use case.您可以同时使用 Gatsby 和 Next 来制作任何类型的网站,但一个巨大的用例是 CMS 驱动的网站,因为其中大部分内容都是 static。这些工具非常适合该用例。
In typical CMS sites, you will have elements that are global - header, footer, search, menu, etc. This is where static generation faces a big challenge: how do I get data into dynamic global components at build time?在典型的 CMS 站点中,您将拥有全局元素 - header、页脚、搜索、菜单等。这就是 static 生成面临的一大挑战:如何在构建时将数据放入动态全局组件中? The answer to this question is... you don't.
这个问题的答案是……你不知道。 And if you think about this for a minute it makes sense.
如果你想一想,这是有道理的。 If you had a 10K page site, would you want to trigger a site-wide rebuild if someone adds a new nav item to a menu?
如果您有一个 10K 页面的站点,如果有人将新的导航项添加到菜单,您是否希望触发站点范围的重建?
So how do we get around this?那么我们如何解决这个问题呢? The best answer I have is
apollo-client
and to do the fetch client side.我的最佳答案是
apollo-client
并执行获取客户端。 This helps us for a number of reasons:这对我们有帮助,原因有很多:
So what does this actually look like?那么这实际上是什么样的呢? At the component level, it looks just like a regular Apollo-enhanced component would.
在组件级别,它看起来就像一个常规的 Apollo 增强组件。 I usually use
styled-components
but I tried to strip that out so you can could better see what's going on.我通常使用
styled-components
但我试图将其剥离,以便您可以更好地了解发生了什么。
import React from 'react'
import { useQuery, gql } from '@apollo/client'
import close from '../public/close.svg'
/**
* <NavMenu>
*
* Just a typical menu you might see on a CMS-driven site. It takes in a couple of props to move state around.
*
* @param { boolean } menuState - lifted state true/false toggle for menu opening/closing
* @param { function } handleMenu - lifted state changer for menuState, handles click event
*/
const NAV_MENU_DATA = gql`
query NavMenu($uid: String!, $lang: String!) {
nav_menu(uid: $uid, lang: $lang) {
main_menu_items {
item {
... on Landing_page {
title
_linkType
_meta {
uid
id
}
}
}
}
}
}
`
const NavMenu = ({ menuState, handleMenu }) => {
// Query for nav menu from Apollo, this is where you pass in your GraphQL variables
const { loading, error, data } = useQuery(NAV_MENU_DATA, {
variables: {
"uid": "nav-menu",
"lang": "en-us"
}
})
if (loading) return `<p>Loading...</p>`;
if (error) return `Error! ${error}`;
// Destructuring the data object
const { nav_menu: { main_menu_items } } = data
// `menuState` checks just make sure out menu was turned on
if (data) return(
<>
<section menuState={ menuState }>
<div>
{ menuState === true && (
<div>Explore</div>
)}
<div onClick={ handleMenu }>
{ menuState === true && (
<svg src={ close } />
)}
</div>
</div>
{ menuState === true && (
<ul>
{ data.map( (item) => {
return (
<li link={ item }>
{ item.title }
</li>
)
})}
</ul>
)}
</section>
</>
)
}
export default NavMenu
This is actually really well documented by the Next.js team, which makes me feel like I'm not totally hacking the way this tool is supposed to work.实际上,Next.js 团队对此进行了很好的记录,这让我觉得我并没有完全破解这个工具的工作方式。 You can find great examples of using Apollo in their repo.
你可以在他们的 repo 中找到使用 Apollo 的很好的例子。
Steps to get Apollo into a Next app:让 Apollo 进入下一个应用程序的步骤:
useApollo
hook that sets up the connection to your data source (I put mine in /lib/apollo/apolloClient.js
within Next's hierarchy but I'm sure it could go elsewhere).useApollo
挂钩来设置与您的数据源的连接(我将我的挂钩放在 Next 的层次结构中的/lib/apollo/apolloClient.js
中,但我确信它可以在其他地方 go )。import { useMemo } from 'react'
import { ApolloClient, InMemoryCache, SchemaLink, HttpLink } from '@apollo/client'
let apolloClient
// This is mostly from next.js official repo on how best to integrate Next and Apollo
function createIsomorphLink() {
// only if you need to do auth
if (typeof window === 'undefined') {
// return new SchemaLink({ schema })
return null
}
// This sets up the connection to your endpoint, will vary widely.
else {
return new HttpLink({
uri: `https://yourendpoint.io/graphql`
})
}
}
// Function that leverages ApolloClient setup, you could just use this and skip the above function if you aren't doing any authenticated routes
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: createIsomorphLink(),
cache: new InMemoryCache(),
})
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract()
// Restore the cache using the data passed from getStaticProps/getServerSideProps
// combined with the existing cached data
_apolloClient.cache.restore({ ...existingCache, ...initialState })
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
// This is goal, now we have a custom hook we can use to set up Apollo across our app. Make sure to export this!
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}
_app.js
in the /pages/
directory of Next./pages/
目录下的_app.js
。 This is basically the wrapper that goes around every page in Next.import { ApolloProvider } from '@apollo/react-hooks'
import { useApollo } from '../lib/apollo/apolloClient'
/**
* <MyApp>
*
* This is an override of the default _app.js setup Next.js uses
*
* <ApolloProvider> gives components global access to GraphQL data fetched in the components (like menus)
*
*/
const MyApp = ({ Component, pageProps }) => {
// Instantiates Apollo client, reads Next.js props and initialized Apollo with them - this caches data into Apollo.
const apolloClient = useApollo(pageProps.initialApolloState)
return (
<ApolloProvider client={ apolloClient }>
<Component {...pageProps} />
</ApolloProvider>
)
}
export default MyApp
And now you can get dynamic data inside of your components using Apollo;现在您可以使用 Apollo 获取组件内部的动态数据; So easy right ;) HA!
太简单了吧 ;) 哈!
For global data fetching in NextJS, I use react-query
and there is no need for a global state because it lets you to cache the data.对于 NextJS 中的全局数据获取,我使用
react-query
并且不需要全局 state 因为它可以让你缓存数据。 Let's say you have a blog with categories and you want to put the category names in the navbar
as a dropdown menu.假设您有一个包含类别的博客,并且您希望将类别名称作为下拉菜单放在
navbar
栏中。 In this case you can call the API to fetch the data with react-query
from the navbar
component and cache it.在这种情况下,您可以调用 API 从
navbar
组件中使用react-query
获取数据并将其缓存。 The navbar data will be available for all pages.导航栏数据将可用于所有页面。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.