[英]Best practice for Next.js data fetching inside a component
我有一個全局顯示的菜單組件。 將數據導入該組件的最佳做法是什么?
我正在嘗試利用 Next.js 提供的 static 代,但 Next.js 團隊的所有數據獲取指南都與頁面相關。 getStaticProps
和getStaticPaths
似乎與頁面生成有關,而不是組件數據。 他們的SWR
package 是正確答案,還是 Apollo Client?
通常在基於鈎子的 React 中,我只是將我的數據調用放入useEffect
中。 我不確定如何推斷出所有內容都是在構建時使用 Next 呈現的。
這是一個如此棘手的問題,我認為我們需要在解決方案成為焦點之前布置一些背景。 我專注於 React.js 世界,但我認為其中很多都適用於 Vue/Nuxt。
Gatsby 和 Next 專注於生成 static 個頁面,這極大地提高了 React.js 站點的性能和 SEO。 除了這個簡單的洞察力之外,這兩個平台都有很多技術開銷,但讓我們從這個數字機器為瀏覽器抽出精美的 HTML 頁面的想法開始。
對於 Next.js(從v9.5
開始),他們的數據獲取機制getStaticProps
為您完成了大部分繁重的工作,但它被沙盒化到/pages/
目錄。 這個想法是它為您獲取數據並在構建期間將其告訴 Node 中的 Next.js 頁面生成器(而不是在useEffect
掛鈎 - 或componentDidMount
中在組件端執行)。 Gatsby 對他們的gatsby-node.js
文件做了很多相同的事情,該文件與 Node 服務器協調頁面構建的數據獲取。
您可以同時使用 Gatsby 和 Next 來制作任何類型的網站,但一個巨大的用例是 CMS 驅動的網站,因為其中大部分內容都是 static。這些工具非常適合該用例。
在典型的 CMS 站點中,您將擁有全局元素 - header、頁腳、搜索、菜單等。這就是 static 生成面臨的一大挑戰:如何在構建時將數據放入動態全局組件中? 這個問題的答案是……你不知道。 如果你想一想,這是有道理的。 如果您有一個 10K 頁面的站點,如果有人將新的導航項添加到菜單,您是否希望觸發站點范圍的重建?
那么我們如何解決這個問題呢? 我的最佳答案是apollo-client
並執行獲取客戶端。 這對我們有幫助,原因有很多:
那么這實際上是什么樣的呢? 在組件級別,它看起來就像一個常規的 Apollo 增強組件。 我通常使用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
實際上,Next.js 團隊對此進行了很好的記錄,這讓我覺得我並沒有完全破解這個工具的工作方式。 你可以在他們的 repo 中找到使用 Apollo 的很好的例子。
讓 Apollo 進入下一個應用程序的步驟:
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
}
/pages/
目錄下的_app.js
。 這基本上是 Next 中每個頁面的包裝器。 我們將向其中添加 Apollo 提供程序,現在我們可以從任何組件全局訪問 Apollo。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
現在您可以使用 Apollo 獲取組件內部的動態數據; 太簡單了吧 ;) 哈!
對於 NextJS 中的全局數據獲取,我使用react-query
並且不需要全局 state 因為它可以讓你緩存數據。 假設您有一個包含類別的博客,並且您希望將類別名稱作為下拉菜單放在navbar
欄中。 在這種情況下,您可以調用 API 從navbar
組件中使用react-query
獲取數據並將其緩存。 導航欄數據將可用於所有頁面。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.