Patterns and Best Practices

React ve Next.js'de veri almak için önerilen birkaç kalıp ve en iyi uygulamalar vardır. Bu sayfa, en yaygın kalıplardan bazılarını ve bunların nasıl kullanılacağını ele alacaktır.

Fetching data on the server

Mümkün olduğunda, Sunucu Bileşenleri ile sunucudan veri almanızı öneririz. Bu size şunları sağlar:

Ardından, Sunucu Eylemleri ile verileri değiştirebilir veya güncelleyebilirsiniz.

Fetching data where it's needed

Bir ağaçtaki birden fazla bileşende aynı veriyi (örneğin mevcut kullanıcı) kullanmanız gerekiyorsa, verileri global olarak getirmeniz veya bileşenler arasında prop'ları iletmeniz gerekmez. Bunun yerine, aynı veri için birden fazla istekte bulunmanın performans etkileri hakkında endişelenmeden veriye ihtiyaç duyan bileşende fetch veya React cache kullanabilirsiniz.

Bu mümkündür çünkü fetch talepleri otomatik olarak not edilir. İstek notu oluşturma hakkında daha fazla bilgi edinin

Bilmekte fayda var: Bir üst düzen ile onun alt düzenleri arasında veri aktarımı mümkün olmadığından, bu durum düzenler için de geçerlidir.

Streaming

Streaming ve Suspense , kullanıcı arayüzünün render edilmiş birimlerini aşamalı olarak render etmenize ve istemciye aşamalı olarak aktarmanıza olanak tanıyan React özellikleridir.

Sunucu Bileşenleri ve iç içe düzenlerle, sayfanın özellikle veri gerektirmeyen kısımlarını anında oluşturabilir ve sayfanın veri getiren kısımları için bir yükleme durumu gösterebilirsiniz. Bu, kullanıcının sayfayla etkileşime geçmeden önce sayfanın tamamının yüklenmesini beklemek zorunda kalmayacağı anlamına gelir.

Server Rendering with Streaming

Akış ve Askıya Alma hakkında daha fazla bilgi edinmek için Yükleme Kullanıcı Arayüzü ve Akış ve Askıya Alma sayfalarına bakın.

Parallel and sequential data fetching

React bileşenleri içinde veri getirirken, iki veri getirme modelinin farkında olmanız gerekir: Paralel ve Sıralı.

Sequential and Parallel Data Fetching

Sequential Data Fetching

İç içe geçmiş bileşenleriniz varsa ve her bileşen kendi verilerini getiriyorsa, bu veri istekleri farklıysa veri getirme işlemi sırayla gerçekleşir (bu, otomatik olarak memoize edildikleri için aynı veriler için yapılan istekler için geçerli değildir).

Örneğin, Playlists bileşeni ancak Artist bileşeni veri almayı tamamladıktan sonra veri almaya başlayacaktır çünkü Playlists, artistID prop'una bağlıdır:

app/artist/[username]/page.tsx
// ...
 
async function Playlists({ artistID }: { artistID: string }) {
  // Wait for the playlists
  const playlists = await getArtistPlaylists(artistID)
 
  return (
    <ul>
      {playlists.map((playlist) => (
        <li key={playlist.id}>{playlist.name}</li>
      ))}
    </ul>
  )
}
 
export default async function Page({
  params: { username },
}: {
  params: { username: string }
}) {
  // Wait for the artist
  const artist = await getArtist(username)
 
  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <Playlists artistID={artist.id} />
      </Suspense>
    </>
  )
}

Bu gibi durumlarda şunları kullanabilirsiniz loading.js (rota segmentleri için) veya React <Suspense> (iç içe geçmiş bileşenler için), React sonuçta akış yaparken anlık bir yükleme durumu göstermek için.

Bu, tüm rotanın veri getirme nedeniyle engellenmesini önleyecek ve kullanıcı sayfanın engellenmeyen bölümleriyle etkileşime girebilecektir.

Veri Taleplerini Engelleme:

Şelaleleri önlemek için alternatif bir yaklaşım, uygulamanızın kökünde global olarak veri getirmektir, ancak bu, verilerin yüklenmesi bitene kadar altındaki tüm rota segmentleri için oluşturmayı engelleyecektir. Bu, "ya hep ya hiç" veri getirme olarak tanımlanabilir. Ya sayfanız ya da uygulamanız için tüm verilere sahip olursunuz ya da hiçbirine sahip olmazsınız.

await içeren tüm getirme istekleri, bir <Suspense> sınırına sarılmadıkları veya loading.js kullanılmadıkları sürece, altındaki tüm ağacın oluşturulmasını ve veri getirilmesini engelleyecektir. Diğer bir alternatif ise paralel veri getirme veya ön yükleme modelini kullanmaktır.

Parallel Data Fetching

Verileri paralel olarak almak için istekleri, verileri kullanan bileşenlerin dışında tanımlayarak ve ardından bileşenin içinden çağırarak istekleri istekli bir şekilde başlatabilirsiniz. Bu, her iki isteği paralel olarak başlatarak zaman kazandırır, ancak kullanıcı her iki söz de çözümlenene kadar işlenen sonucu göremez.

Aşağıdaki örnekte, getArtist ve getArtistAlbums işlevleri Page bileşeninin dışında tanımlanır, ardından bileşenin içinde çağrılır ve her iki vaadin de çözümlenmesi beklenir:

app/artist/[username]/page.tsx
import Albums from './albums'
 
async function getArtist(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}`)
  return res.json()
}
 
async function getArtistAlbums(username: string) {
  const res = await fetch(`https://api.example.com/artist/${username}/albums`)
  return res.json()
}
 
export default async function Page({
  params: { username },
}: {
  params: { username: string }
}) {
  // Initiate both requests in parallel
  const artistData = getArtist(username)
  const albumsData = getArtistAlbums(username)
 
  // Wait for the promises to resolve
  const [artist, albums] = await Promise.all([artistData, albumsData])
 
  return (
    <>
      <h1>{artist.name}</h1>
      <Albums list={albums}></Albums>
    </>
  )
}

Kullanıcı deneyimini iyileştirmek için, render çalışmasını bölmek ve sonucun bir kısmını mümkün olan en kısa sürede göstermek üzere bir Askıya Alma Sınırı ekleyebilirsiniz.

Preloading Data

Şelaleleri önlemenin bir başka yolu da ön yükleme modelini kullanmaktır. Paralel veri getirmeyi daha da optimize etmek için isteğe bağlı olarak bir preload işlevi oluşturabilirsiniz. Bu yaklaşımla, vaatleri prop olarak aktarmak zorunda kalmazsınız. preload işlevi de bir API değil, bir kalıp olduğu için herhangi bir isme sahip olabilir.

components/Item.tsx
import { getItem } from '@/utils/get-item'
 
export const preload = (id: string) => {
  // void evaluates the given expression and returns undefined
  // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
  void getItem(id)
}
export default async function Item({ id }: { id: string }) {
  const result = await getItem(id)
  // ...
}
app/item/[id]/page.tsx
import Item, { preload, checkIsAvailable } from '@/components/Item'
 
export default async function Page({
  params: { id },
}: {
  params: { id: string }
}) {
  // starting loading item data
  preload(id)
  // perform another asynchronous task
  const isAvailable = await checkIsAvailable()
 
  return isAvailable ? <Item id={id} /> : null
}

Using React cache, server-only, and the Preload Pattern

Uygulamanızın tamamında kullanılabilecek bir veri getirme yardımcı programı oluşturmak için cache işlevini, preload kalıbını ve server-only paketini birleştirebilirsiniz.

utils/get-item.ts
import { cache } from 'react'
import 'server-only'
 
export const preload = (id: string) => {
  void getItem(id)
}
 
export const getItem = cache(async (id: string) => {
  // ...
})

Bu yaklaşımla, istekli bir şekilde veri getirebilir, yanıtları önbelleğe alabilir ve bu veri getirme işleminin yalnızca sunucuda gerçekleşmesini garanti edebilirsiniz.

utils/get-item dışa aktarımları, bir öğenin verilerinin ne zaman alınacağını kontrol etmelerini sağlamak için Düzenler, Sayfalar veya diğer bileşenler tarafından kullanılabilir.

Bilmekte fayda var:

  • Sunucu veri getirme işlevlerinin istemcide asla kullanılmadığından emin olmak için server-only paketinikullanmanızı öneririz.

Preventing sensitive data from being exposed to the client

React'in taint API'lerini kullanmanızı öneririz, taintObjectReference ve taintUniqueValue tüm nesne örneklerinin veya hassas değerlerin istemciye aktarılmasını önlemek için.

Uygulamanızda lekelenmeyi etkinleştirmek için Next.js Config experimental.taint seçeneğini true olarak ayarlayın:

next.config.js
module.exports = {
  experimental: {
    taint: true,
  },
}

Ardından, lekelemek istediğiniz nesneyi veya değeri experimental_taintObjectReference veya experimental_taintUniqueValue işlevlerine aktarın:

app/utils.ts
import { queryDataFromDB } from './api'
import {
  experimental_taintObjectReference,
  experimental_taintUniqueValue,
} from 'react'
 
export async function getUserData() {
  const data = await queryDataFromDB()
  experimental_taintObjectReference(
    'Do not pass the whole user object to the client',
    data
  )
  experimental_taintUniqueValue(
    "Do not pass the user's phone number to the client",
    data,
    data.phoneNumber
  )
  return data
}
app/page.tsx
import { getUserData } from './data'
 
export async function Page() {
  const userData = getUserData()
  return (
    <ClientComponent
      user={userData} // this will cause an error because of taintObjectReference
      phoneNumber={userData.phoneNumber} // this will cause an error because of taintUniqueValue
    />
  )
}

Güvenlik ve Sunucu Eylemleri hakkında daha fazla bilgi için .