Server and Client Composition Patterns
React uygulamaları oluştururken, uygulamanızın hangi bölümlerinin sunucuda veya istemcide işlenmesi gerektiğini göz önünde bulundurmanız gerekecektir. Bu sayfa, Sunucu ve İstemci Bileşenlerini kullanırken önerilen bazı kompozisyon modellerini kapsar.
When to use Server and Client Components?
İşte Sunucu ve İstemci Bileşenleri için farklı kullanım durumlarının hızlı bir özeti:
What do you need to do? | Server Component | Client Component |
---|---|---|
Fetch data | ||
Access backend resources (directly) | ||
Keep sensitive information on the server (access tokens, API keys, etc) | ||
Keep large dependencies on the server / Reduce client-side JavaScript | ||
Add interactivity and event listeners (onClick() , onChange() , etc) |
||
Use State and Lifecycle Effects (useState() , useReducer() ,
useEffect() , etc)
|
||
Use browser-only APIs | ||
Use custom hooks that depend on state, effects, or browser-only APIs | ||
Use React Class components |
Server Component Patterns
İstemci tarafı oluşturmayı tercih etmeden önce, sunucuda veri alma veya veritabanınıza ya da arka uç hizmetlerinize erişme gibi bazı işler yapmak isteyebilirsiniz.
Sunucu Bileşenleri ile çalışırken bazı yaygın kalıplar aşağıda verilmiştir:
Sharing data between components
Sunucudan veri alırken, verileri farklı bileşenler arasında paylaşmanız gereken durumlar olabilir. Örneğin, aynı verilere bağlı olan bir düzeniniz ve bir sayfanız olabilir.
React Context
(sunucuda mevcut değildir) kullanmak veya verileri prop olarak iletmek yerine
fetch
veya React'in cache
işlevini kullanarak aynı veriyi, aynı veri için yinelenen isteklerde bulunma endişesi olmadan,
ona ihtiyaç duyan bileşenlerden alabilirsiniz. Bunun nedeni, React'in veri isteklerini otomatik olarak memoize etmek için
fetch
'u genişletmesi ve fetch
mevcut olmadığında
cache
işlevinin kullanılabilmesidir.
React'te memoization hakkında daha fazla bilgi edinin.
Keeping Server-only Code out of the Client Environment
JavaScript modülleri hem Sunucu hem de İstemci Bileşenleri modülleri arasında paylaşılabildiğinden, yalnızca sunucuda çalıştırılması amaçlanan kodun istemciye gizlice girmesi mümkündür.
Örneğin, aşağıdaki veri alma fonksiyonunu ele alalım:
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
})
return res.json()
}
İlk bakışta, getData
hem sunucu hem de istemci üzerinde çalışıyor gibi görünmektedir. Ancak, bu fonksiyon sadece
sunucuda çalıştırılmak üzere yazılmış bir API_KEY
içermektedir.
API_KEY
ortam değişkeni NEXT_PUBLIC
ile ön ek almadığından, yalnızca sunucudan
erişilebilen özel bir değişkendir. Ortam değişkenlerinizin istemciye sızdırılmasını önlemek için Next.js özel ortam değişkenlerini boş bir
dizeyle değiştirir.
Sonuç olarak, getData()
içe aktarılabilse ve istemcide çalıştırılabilse bile, beklendiği gibi çalışmayacaktır.
Değişkeni herkese açık hale getirmek işlevin istemcide çalışmasını sağlayacak olsa da, hassas bilgileri istemciye ifşa etmek
istemeyebilirsiniz.
Sunucu kodunun bu tür istenmeyen istemci kullanımını önlemek için, server-only
paketini kullanarak diğer
geliştiricilerin bu modüllerden birini yanlışlıkla bir İstemci Bileşenine aktarmaları durumunda derleme zamanı hatası vermelerini
sağlayabiliriz.
server-only
adresini kullanmak için önce paketi yükleyin:
npm install server-only
Ardından paketi yalnızca sunucu kodu içeren herhangi bir modüle içe aktarın:
import 'server-only'
export async function getData() {
const res = await fetch('https://external-service.com/data', {
headers: {
authorization: process.env.API_KEY,
},
})
return res.json()
}
Artık, getData()
adresini içe aktaran herhangi bir İstemci Bileşeni, bu modülün yalnızca sunucuda
kullanılabileceğini açıklayan bir derleme zamanı hatası alacaktır.
İlgili paket client-only
, yalnızca istemci kodu içeren modülleri işaretlemek için kullanılabilir - örneğin,
window
nesnesine erişen kod.
Using Third-party Packages and Providers
Sunucu Bileşenleri yeni bir React özelliği olduğundan, ekosistemdeki üçüncü taraf paketler ve sağlayıcılar
useState
, useEffect
ve createContext
gibi yalnızca istemci
özelliklerini kullanan bileşenlere "use client"
yönergesini eklemeye yeni başlıyor.
Günümüzde, yalnızca istemci özelliklerini kullanan npm
paketlerindeki birçok bileşen henüz yönergeye sahip
değildir. Bu üçüncü taraf bileşenler, "use client"
yönergesine sahip oldukları için İstemci Bileşenleri içinde
beklendiği gibi çalışacak, ancak Sunucu Bileşenleri içinde çalışmayacaktır.
Örneğin, <Carousel />
bileşenine sahip varsayımsal acme-carousel
paketini
yüklediğinizi varsayalım. Bu bileşen useState
adresini kullanır, ancak henüz
"use client"
yönergesine sahip değildir.
Bir İstemci Bileşeni içinde <Carousel />
adresini kullanırsanız, beklendiği gibi çalışacaktır:
'use client'
import { useState } from 'react'
import { Carousel } from 'acme-carousel'
export default function Gallery() {
let [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(true)}>View pictures</button>
{/* Works, since Carousel is used within a Client Component */}
{isOpen && <Carousel />}
</div>
)
}
Ancak, bunu doğrudan bir Sunucu Bileşeni içinde kullanmaya çalışırsanız bir hata görürsünüz:
import { Carousel } from 'acme-carousel'
export default function Page() {
return (
<div>
<p>View pictures</p>
{/* Error: `useState` can not be used within Server Components */}
<Carousel />
</div>
)
}
Bunun nedeni Next.js'nin <Carousel />
'un yalnızca istemci özelliklerini kullandığını bilmemesidir.
Bunu düzeltmek için, yalnızca istemci özelliklerine dayanan üçüncü taraf bileşenleri kendi İstemci Bileşenlerinize sarabilirsiniz:
'use client'
import { Carousel } from 'acme-carousel'
export default Carousel
Artık <Carousel />
adresini doğrudan bir Sunucu Bileşeni içinde kullanabilirsiniz:
import Carousel from './carousel'
export default function Page() {
return (
<div>
<p>View pictures</p>
{/* Works, since Carousel is a Client Component */}
<Carousel />
</div>
)
}
Çoğu üçüncü taraf bileşenini sarmalamanız gerekeceğini düşünmüyoruz, çünkü bunları büyük olasılıkla İstemci Bileşenleri içinde kullanacaksınız. Bununla birlikte, React state ve context'e dayandıkları ve genellikle bir uygulamanın kökünde ihtiyaç duydukları için sağlayıcılar bir istisnadır. Üçüncü taraf bağlam sağlayıcıları hakkında daha fazla bilgiyi aşağıda bulabilirsiniz.
Using Context Providers
Context sağlayıcıları, geçerli tema gibi global endişeleri paylaşmak için genellikle bir uygulamanın köküne yakın bir yerde oluşturulur. React bağlamı Sunucu Bileşenlerinde desteklenmediğinden, uygulamanızın kökünde bir bağlam oluşturmaya çalışmak hataya neden olacaktır:
import { createContext } from 'react'
// createContext is not supported in Server Components
export const ThemeContext = createContext({})
export default function RootLayout({ children }) {
return (
<html>
<body>
<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
</body>
</html>
)
}
Bunu düzeltmek için, bağlamınızı oluşturun ve sağlayıcısını bir İstemci Bileşeninin içinde oluşturun:
'use client'
import { createContext } from 'react'
export const ThemeContext = createContext({})
export default function ThemeProvider({
children,
}: {
children: React.ReactNode
}) {
return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}
Sunucu Bileşeniniz artık bir İstemci Bileşeni olarak işaretlendiğinden sağlayıcınızı doğrudan oluşturabilecektir:
import ThemeProvider from './theme-provider'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}
Sağlayıcı kökte işlendiğinde, uygulamanızdaki diğer tüm İstemci Bileşenleri bu bağlamı kullanabilecektir.
Bilmekte fayda var: Sağlayıcıları ağaçta mümkün olduğunca derinde oluşturmalısınız -
ThemeProvider
adresinin<html>
belgesinin tamamı yerine yalnızca{children}
adresini nasıl sardığına dikkat edin. Bu, Next.js'nin Sunucu Bileşenlerinizin statik kısımlarını optimize etmesini kolaylaştırır.
Advice for Library Authors
Benzer bir şekilde, diğer geliştiriciler tarafından tüketilecek paketler oluşturan kütüphane yazarları, paketlerinin istemci giriş noktalarını
işaretlemek için "use client"
yönergesini kullanabilirler. Bu, paket kullanıcılarının paket bileşenlerini bir
sarmalama sınırı oluşturmak zorunda kalmadan doğrudan kendi Sunucu Bileşenlerine aktarmalarını sağlar.
Ağacın daha derinlerinde 'use client' kullanarak paketinizi optimize edebilir ve içe aktarılan modüllerin Sunucu Bileşeni modül grafiğinin bir parçası olmasına izin verebilirsiniz.
Bazı paketleyicilerin "use client"
yönergelerini çıkarabileceğini belirtmek gerekir.
React Wrap Balancer
ve
Vercel Analytics
depolarında esbuild'in "use client"
yönergesini içerecek şekilde nasıl yapılandırılacağına dair bir örnek
bulabilirsiniz.
Client Components
Moving Client Components Down the Tree
İstemci JavaScript paketinin boyutunu azaltmak için İstemci Bileşenlerini bileşen ağacınızda aşağıya taşımanızı öneririz.
Örneğin, statik öğeler (örn. logo, bağlantılar, vb.) ve durum kullanan etkileşimli bir arama çubuğu içeren bir Düzeniniz olabilir.
Tüm düzeni bir İstemci Bileşeni yapmak yerine, etkileşimli mantığı bir İstemci Bileşenine taşıyın (örneğin
<SearchBar />
) ve düzeninizi bir Sunucu Bileşeni olarak tutun. Bu, düzenin tüm bileşen Javascript'ini
istemciye göndermek zorunda olmadığınız anlamına gelir.
// SearchBar is a Client Component
import SearchBar from './searchbar'
// Logo is a Server Component
import Logo from './logo'
// Layout is a Server Component by default
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Logo />
<SearchBar />
</nav>
<main>{children}</main>
</>
)
}
Passing props from Server to Client Components (Serialization)
Bir Sunucu Bileşeninde veri alırsanız, verileri İstemci Bileşenlerine prop olarak aktarmak isteyebilirsiniz. Sunucudan İstemci Bileşenlerine aktarılan prop'ların React tarafından serileştirilebilir olması gerekir.
İstemci Bileşenleriniz serileştirilemeyen verilere bağlıysa, üçüncü taraf bir kütüphane ile istemciden veya bir Rota İşleyicisi aracılığıyla sunucudan veri getirebilirsiniz.
Interleaving Server and Client Components
İstemci ve Sunucu Bileşenlerini bir araya getirirken, kullanıcı arayüzünüzü bir bileşen ağacı olarak görselleştirmek yararlı olabilir. Bir
Sunucu Bileşeni olan
kök düzeninden
başlayarak, "use client"
yönergesini ekleyerek istemcide bileşenlerin belirli alt ağaçlarını oluşturabilirsiniz.
Bu istemci alt ağaçları içinde, Sunucu Bileşenlerini yuvalayabilir veya Sunucu Eylemlerini çağırabilirsiniz, ancak akılda tutulması gereken bazı şeyler vardır:
- İstek-yanıt yaşam döngüsü sırasında kodunuz sunucudan istemciye geçer. İstemcideyken sunucudaki verilere veya kaynaklara erişmeniz gerekirse, sunucuya yeni bir istekte bulunacaksınız - ileri geri geçiş yapmayacaksınız.
- Sunucuya yeni bir istek yapıldığında, İstemci Bileşenleri içinde yuvalanmış olanlar da dahil olmak üzere tüm Sunucu Bileşenleri ilk olarak işlenir. Render edilen sonuç (RSC Payload), İstemci Bileşenlerinin konumlarına referanslar içerecektir. Ardından, istemcide React, Sunucu ve İstemci Bileşenlerini tek bir ağaçta uzlaştırmak için RSC Payload'u kullanır.
-
İstemci Bileşenleri Sunucu Bileşenlerinden sonra oluşturulduğu için, bir Sunucu Bileşenini bir İstemci Bileşeni modülüne aktaramazsınız (çünkü bu sunucuya yeni bir istek yapılmasını gerektirir). Bunun yerine, bir Sunucu Bileşenini
props
olarak bir İstemci Bileşenine aktarabilirsiniz. Aşağıdaki desteklenmeyen kalıp ve desteklenen kalıpbölümlerine bakın.
Unsupported Pattern: Importing Server Components into Client Components
Aşağıdaki model desteklenmez. Bir Sunucu Bileşenini bir İstemci Bileşenine aktaramazsınız:
'use client'
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
<ServerComponent />
</>
)
}
Supported Pattern: Passing Server Components to Client Components as Props
Aşağıdaki model desteklenmektedir. Sunucu Bileşenlerini bir İstemci Bileşenine prop olarak aktarabilirsiniz.
Yaygın bir model, İstemci Bileşeninizde bir "yuva" oluşturmak için React children
prop'unu kullanmaktır.
Aşağıdaki örnekte, <ClientComponent>
bir children
prop kabul etmektedir:
'use client'
import { useState } from 'react'
export default function ClientComponent({
children,
}: {
children: React.ReactNode
}) {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount(count + 1)}>{count}</button>
{children}
</>
)
}
<ClientComponent>
children
adresinin eninde sonunda bir Sunucu Bileşeninin
sonucu tarafından doldurulacağını bilmez. <ClientComponent>
'un sahip olduğu tek sorumluluk
children
'un eninde sonunda nereye yerleştirileceğine karar vermektir.
Bir üst Sunucu Bileşeninde, hem <ClientComponent>
hem de
<ServerComponent>
öğelerini içe aktarabilir ve <ServerComponent>
öğesini
<ClientComponent>
öğesinin bir alt öğesi olarak geçirebilirsiniz:
// This pattern works:
// You can pass a Server Component as a child or prop of a
// Client Component.
import ClientComponent from './client-component'
import ServerComponent from './server-component'
// Pages in Next.js are Server Components by default
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}
Bu yaklaşımla <ClientComponent>
ve <ServerComponent>
birbirinden ayrılır
ve bağımsız olarak render edilebilir. Bu durumda, <ServerComponent>
alt öğesi,
<ClientComponent>
istemcide oluşturulmadan çok önce sunucuda oluşturulabilir.
Bilmekte fayda var:
- "İçeriği yukarı kaldırma" modeli, bir üst bileşen yeniden render edildiğinde iç içe geçmiş bir alt bileşenin yeniden render edilmesini önlemek için kullanılmıştır.
children
prop'u ile sınırlı değilsiniz. JSX iletmek için herhangi bir prop kullanabilirsiniz.