title |
---|
Bileşenleri Saf Tutmak |
Bazı JavaScript fonksiyonları saf olarak adlandırılır. Saf fonksiyonlar sadece bir hesaplama yaparlar ve başka bir işlem gerçekleştirmezler. Bileşenlerinizi sadece saf fonksiyonlar olarak yazarak, kod tabanınız büyüdükçe ortaya çıkabilecek birçok karmaşık hatayı ve öngörülemeyen davranışları önleyebilirsiniz. Ancak, bu faydaları elde etmek için bazı kurallara uymalısınız.
- Saflık nedir ve hatalardan kaçınmanıza nasıl yardımcı olur,
- Değişiklikleri render aşaması dışında tutarak bileşenleri nasıl saf tutabileceğiniz,
- Bileşenlerinizdeki hataları bulmak için Strict Modu'u nasıl kullanacağınız.
Bilgisayar biliminde (ve özellikle fonksiyonel programlama dünyasında), saf bir fonksiyon aşağıdaki özelliklere sahip fonksiyonlardır:
- Kendi işine bakar. Çağrılmadan önce var olan herhangi bir nesneyi ve objeyi değiştirmez.
- Aynı girdi, aynı çıktı. Aynı girdiler verildiğinde, saf bir fonksiyon her zaman aynı sonucu döndürmelidir.
Saf fonksiyonların bir örneğini zaten biliyor olabilirsiniz: matematikteki formüller.
Bu formülü ele alalım: y = 2x.
Eğer x = 2 ise y = 4'tür. Her zaman.
Eğer x = 3 ise y = 6'dır. Her zaman.
Eğer x = 3 ise, y günün zamanına veya borsanın durumuna bağlı olarak bazen 9 ya da –1 veya 2.5 olmaz.
Eğer y = 2x ve x = 3 ise, y her zaman 6'dır.
Eğer bunu bir JavaScript fonksiyonuna çevirseydik, şöyle görünürdü:
function double(number) {
return 2 * number;
}
Yukardaki örnekte, double
saf bir fonksiyondur. Fonksiyona 3
parametresini geçerseniz, 6'yı
döndürür. Her zaman.
React bu konseptin etrafında tasarlanmıştır. React yazdığınız her bileşenin saf bir fonksiyon olduğunu varsayar. Bu, yazdığınız React bileşenlerinin, aynı girdiler verildiğinde her zaman aynı JSX'i döndürmesi gerektiği anlamına gelir:
function Recipe({ drinkers }) {
return (
<ol>
<li>Boil {drinkers} cups of water.</li>
<li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
<li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
</ol>
);
}
export default function App() {
return (
<section>
<h1>Spiced Chai Recipe</h1>
<h2>For two</h2>
<Recipe drinkers={2} />
<h2>For a gathering</h2>
<Recipe drinkers={4} />
</section>
);
}
Drinkers
parametresine {2}
değerini verip, Recipe'ye
geçerseniz, 2 bardak su
içeren JSX'i döndürür. Her zaman.
Drinkers
parametresine {4}
değerini verip, 4 bardak su
içeren JSX’i döndürür. Her zaman.
Tıpkı bir matematik formülü gibi.
Bileşenlerinizi de bir tarif gibi düşünebilirsiniz: bunları takip eder ve pişirme esnasında yeni malzemeler eklemezseniz, her zaman aynı yemeği yaparsınız. Bu “yemek”, bileşenin React’e render için sağladığı JSX’tir.
React'in render işlemi her zaman saf olmalıdır. Bileşenler yalnızca JSX'lerini döndürmeli, ve render işleminden önce var olan herhangi bir nesne veya değişkeni değiştirmemelidir - aksi takdirde bileşenler saf olmaktan çıkar!
İşte bu kuralı ihlal eden bir bileşen örneği:
let guest = 0;
function Cup() {
// Bad: changing a preexisting variable!
guest = guest + 1;
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
);
}
Bu bileşen, kendisi dışında tanımlanmış bir misafir
değişkenini okuyup yazıyor. Bu, bu bileşenin birden fazla kez çağrılması farklı JSX üreteceği anlamına gelir! Ve daha da fazlası, diğer bileşenler de misafir
değişkenini okursa, ne zaman render edildiklerine bağlı olarak farklı JSX üreteceklerdir! Bu tahmin edilebilir değil.
y = 2x formülümüze geri dönersek, şimdi x = 2 olsa bile, y = 4'e güvenemeyiz. Testlerimiz başarısız olabilir, kullanıcılarımız şaşkına dönebilir, uçaklar düşebilir - nasıl karışık hatalara neden olacağını görebilirsiniz!
Bunun yerine, misafiri
bir prop olarak geçerek bu bileşeni düzeltebilirsiniz:
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup guest={1} />
<Cup guest={2} />
<Cup guest={3} />
</>
);
}
Artık bileşeniniz saf bir durumda, çünkü döndürdüğü JSX yalnızca misafir
prop’una bağlı.
Genel olarak, bileşenlerinizin belirli bir sırada işlenmesiniz beklememelisiniz. y = 2x'i, y = 5x'ten önce veya sonra çağırmanız farketmez: Her iki formül de birbirinden bağımsız olarak çözülecektir. Aynı şekilde, her bileşen yalnızca "kendi için düşünmeli" ve render işlemi sırasında diğer bileşenlerle koordine etmeye veya onlara bağımlı olmaya çalışmamalıdır. Render işlemi bir okul sınavı gibi: her bileşen kendi JSX'ini hesaplamalıdır!
StrictMode ile saf olmayan hesaplamaları algılama {/detecting-impure-calculations-with-strict-mode/}
Henüz hepsini kullanmamış olsanız da, React'te işleme sırasında okuyabileceğiniz üç tür girdi vardır: props, state, and context. Bu girişleri her zaman salt okunur olarak değerlendirmelisiniz.
Kullanıcı girişine yanıt olarak bir şeyi değiştirmek istediğinizde, bir değişkene yazmak yerine, state oluşturmalısınız. Bileşeniniz render edilirken önceden var olan değişkenleri veya nesneleri asla değiştirmemelisiniz.
React, geliştirme sırasında her bileşenin işlevini iki kez çağırdığı bir “Strict Mode” sunar. Strict Mode, bileşen işlevlerini iki kez çağırarak, bu kuralları çiğneyen bileşenlerin bulunmasına yardımcı olur.
Orijinal örneğin "Guest #2", "Guest #4" ve "Guest #6" yerine "Guest #1", "Guest #2" ve "Guest #3" yerine nasıl görüntülendiğine dikkat edin. Orijinal fonksiyon saf değildi, bu yüzden onu iki kez çağırmak onu bozdu. Ancak sabit saf fonksiyon, işlev her seferinde iki kez çağrılsa bile çalışır. Saf fonksiyonlar yalnızca hesaplama yapar, bu yüzden onları iki kez çağırmak hiçbir şeyi değiştirmez -- tıpkı double(2)
'yi iki kez çağırmanın döndürülen şeyi değiştirmemesi, y = 2x'i iki kez çözmenin y'yi değiştirmemesi gibi. Aynı girdiler, aynı çıktılar. Her zaman.
Strict Mode'un canlıda hiçbir etkisi yoktur, bu nedenle kullanıcılarınız için uygulamayı yavaşlatmaz. Strict Mode'u etkinleştirmek için kök bileşeninizi <React.StrictMode>
içine sarabilirsiniz. Bazı kütüphaneler bunu varsayılan olarak yapar.
Yukarıdaki örnekteki sorun, bileşenin render edilirken önceden var olan bir değişkeni değiştirmesiydi. Bu genellikle biraz korkutucu görünmesi için mutasyon olarak adlandırılır. Saf fonksiyonlar, fonksiyonun kapsamı dışındaki değişkenleri veya çağrıdan önce oluşturulmuş nesneleri değiştirmez - bu onları saf olmayan fonksiyonlar yapar!
Ancak, render işlemi sırasında oluşturduğunuz değişkenleri ve nesneleri değiştirmek tamamen normaldir. Bu örnekte, []
bir dizi oluşturur, bunu bir cups
değişkenine atar ve ardından içine bir düzine fincan eklersiniz
:
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
Eğer cups değişkeni veya []
dizisi TeaGathering
fonksiyonunun dışında oluşturulmuş olsaydı, bu büyük bir sorun olurdu! Bu dizinin içine öğeler ekleyerek önceden var olan bir nesneyi değiştiriyor olacaktınız.
Ancak, TeaGathering
içindeki aynı render işlemi sırasında oluşturduğunuz için bu tamamen normaldir. TeaGathering
dışındaki hiçbir kod bunun olduğunu asla bilemeyecektir. Buna "yerel mutasyon" denir - bileşeninizin küçük bir sırrı gibi.
Fonksiyonel programlama, büyük ölçüde saflığa dayanırken, bir noktada, bir yerde, bir şeyin değişmesi gerekir. Bu, programlamanın bir nevi amacıdır! Ekranın güncellenmesi, bir animasyonun başlatılması, verilerin değiştirilmesi gibi değişikliklere yan etkiler denir. Bunlar, render işlemi sırasında değil, "yan tarafta" meydana gelen şeylerdir.
React'te, yan etkiler genellikle olay yöneticileri içine yazılır. Olay Yöneticileri, bir işlem gerçekleştirdiğinizde (örneğin, bir düğmeye tıkladığınızda) React'ın çalıştırdığı fonksiyonlardır. Olay Yöneticileri bileşeninizin içinde tanımlanmış olsa da, bunlar işleme sırasında çalışmazlar! Bu nedenle olay işleyicilerinin saf olması gerekmez.
Diğer tüm seçenekleri tükettiyseniz ve yan etkiniz için doğru olay yöneticilieri’ni bulamıyorsanız, bileşeninizde biruseEffect
çağrısı ile onu döndürülen JSX'inize hâlâ ekleyebilirsiniz. Bu, React'e onu renderdan yani işlemeden sonra, yan etkilere izin verildiğinde çalıştırmasını söyler. Ancak, bu yaklaşım son çareniz olmalıdır.
Mümkün olduğunda, mantığınızı yalnızca render ile ifade etmeye çalışın. Bunun sizi ne kadar ileri götürebileceğine şaşıracaksınız!
Saf işlevler yazmak biraz alışkanlık ve disiplin gerektirir. Ama aynı zamanda harika fırsatların da kapısını açar:
- Bileşenleriniz farklı bir ortamda, örneğin sunucuda çalışabilir! Aynı girdiler için aynı sonucu döndürdüklerinden, bir bileşen birçok kullanıcı isteğine hizmet edebilir.
- Girişleri değişmeyen bleşenleri render etmeyi atlayarak performansı artırabilirsiniz. Bu güvenlidir çünkü saf işlevler her zaman aynı sonuçları döndürür, bu nedenle önbelleğe alınmaları güvenlidir.
- Derin bir bileşen ağacı render edilirken ortasında bazı veriler değişirse, React, zaman aşımına uğramış enderi bitirmek için zaman kaybetmeden işlemeyi yeniden başlatabilir. Saflık, herhangi bir zamanda hesaplamayı durdurmayı güvenli hale getirir.
İnşa ettiğimiz her yeni React özelliği, saflıktan yararlanır. Veri toplamadan animasyonlara ve performansa kadar, bileşenleri saf tutmak React paradigmasının gücünü açığa çıkarır.
- Bir bileşen saf olmalıdır, yani:
- Kendi işine bakar. İşlemeden önce var olan hiçbir nesneyi veya değişkeni değiştirmemelidir.
- Aynı girdiler, aynı çıktılar. Aynı girdiler verildiğinde, bir bileşen her zaman aynı JSX'i döndürmelidir.
- Oluşturma herhangi bir zamanda gerçekleşebilir, bu nedenle bileşenler birbirinin oluşturma sırasına bağlı olmamalıdır.
- Bileşenlerinizin render için kullandığı girdilerin hiçbirini mutasyona uğratmamalısınız. Buna props, state ve context dahildir. Ekranı güncellemek için, önceden var olan nesneleri değiştirmek yerine state "oluşturun".
- Döndürdüğünüz JSX'te bileşeninizin mantığını ifade etmeye çalışın. "Bir şeyleri değiştirmeniz" gerektiğinde, bunu genellikle bir olay yöneticilerinde yapmak isteyeceksiniz. Son çare olarak,
useEffect
'i kullanabilirsiniz. - Saf fonksiyonlar yazmak biraz pratik gerektirir, ancak React'in paradigmasının gücünü açığa çıkarır.
Bu bileşen, <h1>
'in CSS class'ını gün esnasında, gece yarısından sabah 6'ya kadar "night"
ve diğer tüm zamanlarda ise "day"
olarak ayarlamaya çalışıyor. Ancak, bu işe yaramıyor. Bu bileşeni düzeltebilir misiniz?
Bilgisayarın saat dilimini geçici olarak değiştirerek çözümünüzün çalışıp çalışmadığını doğrulayabilirsiniz. Geçerli saat gece yarısı ile sabah altı arasında olduğunda, saat ters renklere sahip olmalıdır!
Render etmek bir hesaplamadır., bir şeyler "yapmaya" çalışmamalı. Aynı fikri farklı şekilde ifade edebilir misiniz?
export default function Clock({ time }) {
let hours = time.getHours();
if (hours >= 0 && hours <= 6) {
document.getElementById('time').className = 'night';
} else {
document.getElementById('time').className = 'day';
}
return (
<h1 id="time">
{time.toLocaleTimeString()}
</h1>
);
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
export default function App() {
const time = useTime();
return (
<Clock time={time} />
);
}
body > * {
width: 100%;
height: 100%;
}
.day {
background: #fff;
color: #222;
}
.night {
background: #222;
color: #fff;
}
Bu bileşeni, className
değerini hesaplayarak ve bunu render çıktısına dahil ederek düzeltebilirsiniz:
export default function Clock({ time }) {
let hours = time.getHours();
let className;
if (hours >= 0 && hours <= 6) {
className = 'night';
} else {
className = 'day';
}
return (
<h1 className={className}>
{time.toLocaleTimeString()}
</h1>
);
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
export default function App() {
const time = useTime();
return (
<Clock time={time} />
);
}
body > * {
width: 100%;
height: 100%;
}
.day {
background: #fff;
color: #222;
}
.night {
background: #222;
color: #fff;
}
Bu örnekte, yan etki (DOM'u değiştirmek) hiç gerekli değildi. Yalnızca JSX'i döndürmeniz gerekiyordu.
İki Profile
bileşeni, farklı verilerle yan yana oluşturulur. İlk profilde "Daralt"a ve ardından "Genişlet"e basın. Artık her iki profilin de aynı kişiyi gösterdiğini fark edeceksiniz. Bu bir hata.
Hatanın nedenini bulun ve düzeltin.
Hatalı kod Profile.js
'in içindedir. Hepsini yukarıdan aşağıya okuduğunuzdan emin olun!
import Panel from './Panel.js';
import { getImageUrl } from './utils.js';
let currentPerson;
export default function Profile({ person }) {
currentPerson = person;
return (
<Panel>
<Header />
<Avatar />
</Panel>
)
}
function Header() {
return <h1>{currentPerson.name}</h1>;
}
function Avatar() {
return (
<img
className="avatar"
src={getImageUrl(currentPerson)}
alt={currentPerson.name}
width={50}
height={50}
/>
);
}
import { useState } from 'react';
export default function Panel({ children }) {
const [open, setOpen] = useState(true);
return (
<section className="panel">
<button onClick={() => setOpen(!open)}>
{open ? 'Collapse' : 'Expand'}
</button>
{open && children}
</section>
);
}
import Profile from './Profile.js';
export default function App() {
return (
<>
<Profile person={{
imageId: 'lrWQx8l',
name: 'Subrahmanyan Chandrasekhar',
}} />
<Profile person={{
imageId: 'MK3eW3A',
name: 'Creola Katherine Johnson',
}} />
</>
)
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 5px; border-radius: 50%; }
.panel {
border: 1px solid #aaa;
border-radius: 6px;
margin-top: 20px;
padding: 10px;
width: 200px;
}
h1 { margin: 5px; font-size: 18px; }
Sorun Profile
bileşeninin currentPerson
adlı önceden var olan bir değişkene yazması, ve Header
and Avatar
bileşenlerinin bundan okumasıdır. Bu, üçünü de kirli yapar ve tahmin etmeyi zorlaştırır.
Hatayı düzeltmek için, currentPerson
değişkenini kaldırın. Bunun yerine, tüm bilgileri Profile
'den Header
'a ve Avatar
'a props aracılığıyla iletin. Her iki bileşene de bir person
prop'u eklemeniz ve bunu sonuna kadar geçmeniz gerekecek.
import Panel from './Panel.js';
import { getImageUrl } from './utils.js';
export default function Profile({ person }) {
return (
<Panel>
<Header person={person} />
<Avatar person={person} />
</Panel>
)
}
function Header({ person }) {
return <h1>{person.name}</h1>;
}
function Avatar({ person }) {
return (
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={50}
height={50}
/>
);
}
import { useState } from 'react';
export default function Panel({ children }) {
const [open, setOpen] = useState(true);
return (
<section className="panel">
<button onClick={() => setOpen(!open)}>
{open ? 'Collapse' : 'Expand'}
</button>
{open && children}
</section>
);
}
import Profile from './Profile.js';
export default function App() {
return (
<>
<Profile person={{
imageId: 'lrWQx8l',
name: 'Subrahmanyan Chandrasekhar',
}} />
<Profile person={{
imageId: 'MK3eW3A',
name: 'Creola Katherine Johnson',
}} />
</>
);
}
export function getImageUrl(person, size = 's') {
return (
'https://i.imgur.com/' +
person.imageId +
size +
'.jpg'
);
}
.avatar { margin: 5px; border-radius: 50%; }
.panel {
border: 1px solid #aaa;
border-radius: 6px;
margin-top: 20px;
padding: 10px;
width: 200px;
}
h1 { margin: 5px; font-size: 18px; }
React'in bileşen fonksiyonlarının belirli bir sırada yürütüleceğini garanti etmediğini unutmayın, bu nedenle değişkenleri ayarlayarak bunlar arasında iletişim kuramazsınız. Tüm iletişim, proplar aracılığıyla gerçekleşmelidir.
Şirketinizin CEO'su sizden çevrimiçi saat uygulamanıza "hikayeler" eklemenizi istiyor ve siz hayır diyemiyorsunuz. stories
listesini kabul eden bir StoryTray
bileşeni ve ardından bir "Create Story" placeholder'ını yazdınız.
Prop olarak aldığınız stories
dizisinin sonuna bir sahte hikaye daha iterek "Create Story" placeholder'ını uyguladınız. Ancak nedense "Create Story" birden çok kez görünüyor. Sorunu düzeltin.
export default function StoryTray({ stories }) {
stories.push({
id: 'create',
label: 'Create Story'
});
return (
<ul>
{stories.map(story => (
<li key={story.id}>
{story.label}
</li>
))}
</ul>
);
}
import { useState, useEffect } from 'react';
import StoryTray from './StoryTray.js';
let initialStories = [
{id: 0, label: "Ankit's Story" },
{id: 1, label: "Taylor's Story" },
];
export default function App() {
let [stories, setStories] = useState([...initialStories])
let time = useTime();
// İPUCU: Belgeleri okurken hafızanın sonsuza kadar büyümesini önleyin.
// Burada kendi kurallarımızı çiğniyoruz.
if (stories.length > 100) {
stories.length = 100;
}
return (
<div
style={{
width: '100%',
height: '100%',
textAlign: 'center',
}}
>
<h2>It is {time.toLocaleTimeString()} now.</h2>
<StoryTray stories={stories} />
</div>
);
}
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
ul {
margin: 0;
list-style-type: none;
}
li {
border: 1px solid #aaa;
border-radius: 6px;
float: left;
margin: 5px;
margin-bottom: 20px;
padding: 5px;
width: 70px;
height: 100px;
}
{
"hardReloadOnChange": true
}
Saat her güncellendiğinde, "Create Story"nin iki kez nasıl eklendiğine dikkat edin. Bu, oluşturma sırasında bir mutasyona sahip olduğumuza dair bir ipucu görevi görür -- StrictMode, bu sorunları daha belirgin hale getirmek için bileşenleri iki kez çağırır.
StoryTray
fonksiyonu saf değil. Alınan stories
dizisinde (bir prop!) push
'u çağırarak, oluşturmaya başlamadan önce, StoryTray
oluşturulmuş bir nesneyi değiştiriyor. Bu, onu hatalı kılar ve tahmin etmeyi çok zorlaştırır.
En basit düzeltme, diziye hiç dokunmamak ve "Create Story"i ayrı ayrı oluşturmaktır:
export default function StoryTray({ stories }) {
return (
<ul>
{stories.map(story => (
<li key={story.id}>
{story.label}
</li>
))}
<li>Create Story</li>
</ul>
);
}
import { useState, useEffect } from 'react';
import StoryTray from './StoryTray.js';
let initialStories = [
{id: 0, label: "Ankit's Story" },
{id: 1, label: "Taylor's Story" },
];
export default function App() {
let [stories, setStories] = useState([...initialStories])
let time = useTime();
// İPUCU: Belgeleri okurken hafızanın sonsuza kadar büyümesini önleyin.
// Burada kendi kurallarımızı çiğniyoruz.
if (stories.length > 100) {
stories.length = 100;
}
return (
<div
style={{
width: '100%',
height: '100%',
textAlign: 'center',
}}
>
<h2>It is {time.toLocaleTimeString()} now.</h2>
<StoryTray stories={stories} />
</div>
);
}
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
ul {
margin: 0;
list-style-type: none;
}
li {
border: 1px solid #aaa;
border-radius: 6px;
float: left;
margin: 5px;
margin-bottom: 20px;
padding: 5px;
width: 70px;
height: 100px;
}
Alternatif olarak, içine bir öğe göndermeden önce (mevcut olanı kopyalayarak) bir new dizisi oluşturabilirsiniz:
export default function StoryTray({ stories }) {
// Diziyi kopyalayın!
let storiesToDisplay = stories.slice();
// Orijinal diziyi etkilemez:
storiesToDisplay.push({
id: 'create',
label: 'Create Story'
});
return (
<ul>
{storiesToDisplay.map(story => (
<li key={story.id}>
{story.label}
</li>
))}
</ul>
);
}
import { useState, useEffect } from 'react';
import StoryTray from './StoryTray.js';
let initialStories = [
{id: 0, label: "Ankit's Story" },
{id: 1, label: "Taylor's Story" },
];
export default function App() {
let [stories, setStories] = useState([...initialStories])
let time = useTime();
// İPUCU: Belgeleri okurken hafızanın sonsuza kadar büyümesini önleyin.
// Burada kendi kurallarımızı çiğniyoruz.
if (stories.length > 100) {
stories.length = 100;
}
return (
<div
style={{
width: '100%',
height: '100%',
textAlign: 'center',
}}
>
<h2>It is {time.toLocaleTimeString()} now.</h2>
<StoryTray stories={stories} />
</div>
);
}
function useTime() {
const [time, setTime] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(id);
}, []);
return time;
}
ul {
margin: 0;
list-style-type: none;
}
li {
border: 1px solid #aaa;
border-radius: 6px;
float: left;
margin: 5px;
margin-bottom: 20px;
padding: 5px;
width: 70px;
height: 100px;
}
Bu, mutasyonunuzu yerel ve render eden fonksiyonunuzu saf tutar. Ancak yine de dikkatli olmanız gerekir: örneğin, dizinin mevcut öğelerinden herhangi birini değiştirmeye çalışırsanız, bu öğeleri de klonlamanız gerekir.
Dizilerdeki hangi işlemlerin onları değiştirdiğini ve hangilerinin değiştirmediğini hatırlamakta fayda var. Örneğin, push
, pop
, reverse
, ve sort
orijinal diziyi değiştirir, ancak slice
, filter
, ve map
yeni bir dizi oluşturur.