Projelerinizde Kullanabileceğiniz Faydalı React Hook'ları

Yayınlanan: 2022-03-10
Hızlı özet ↬ React sınıfı tabanlı bileşenler dağınık, kafa karıştırıcı, insanlar ve makineler için zordur. Ancak React 16.8'den önce, durumlar, yaşam döngüsü yöntemleri ve diğer birçok önemli işlevsellik gerektiren projeler için sınıf tabanlı bileşenler zorunluydu. Tüm bunlar, React 16.8'deki kancaların tanıtılmasıyla değişti. Kancalar oyunun kurallarını değiştirir. React'i basitleştirdiler, daha düzenli hale getirdiler, yazmayı ve hata ayıklamayı kolaylaştırdılar ve ayrıca öğrenme eğrisini azalttılar.

Kancalar , React özelliklerine bağlanmanıza veya bunlardan yararlanmanıza izin veren basit işlevlerdir. Sınıf bileşenlerinin üç ana sorununu ele almak için React Conf 2018'de tanıtıldılar: sarmalayıcı cehennem, devasa bileşenler ve kafa karıştırıcı sınıflar. Kancalar, React fonksiyonel bileşenlerine güç vererek, onunla bütün bir uygulamanın geliştirilmesini mümkün kılar.

Sınıf bileşenlerinin yukarıda belirtilen problemleri birbirine bağlıdır ve biri olmadan diğeri çözülürse daha fazla problem ortaya çıkabilir. Neyse ki, kancalar, React'te daha ilginç özelliklere yer açarken tüm sorunları basit ve verimli bir şekilde çözdü. Kancalar, halihazırda var olan React kavramlarının ve sınıflarının yerini almaz, yalnızca onlara doğrudan erişmek için bir API sağlarlar.

React ekibi, React 16.8'de birkaç kanca tanıttı. Ancak, uygulamanızda üçüncü taraf sağlayıcıların kancalarını da kullanabilir veya hatta özel bir kanca oluşturabilirsiniz. Bu eğitimde, React'teki bazı kullanışlı kancalara ve bunların nasıl kullanılacağına göz atacağız. Her kancanın birkaç kod örneğini inceleyeceğiz ve ayrıca nasıl özel bir kanca oluşturacağınızı keşfedeceğiz.

Not: Bu eğitim, Javascript (ES6+) ve React hakkında temel bir anlayış gerektirir.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Kancaların Arkasındaki Motivasyon

Daha önce belirtildiği gibi, kancalar üç sorunu çözmek için yaratıldı: sarmalayıcı cehennemi, devasa bileşenler ve kafa karıştırıcı sınıflar. Bunların her birine daha ayrıntılı olarak bakalım.

sarıcı cehennem

Sınıf bileşenleriyle oluşturulmuş karmaşık uygulamalar, kolayca sarmalayıcı cehennemine girer. Uygulamayı React Dev Tools'da incelerseniz, derinlemesine iç içe geçmiş bileşenleri fark edeceksiniz. Bu, bileşenlerle çalışmayı veya bunların hatalarını ayıklamayı çok zorlaştırır. Bu problemler daha yüksek dereceli bileşenler ve render props ile çözülebilse de, kodunuzu biraz değiştirmenizi gerektirir. Bu, karmaşık bir uygulamada karışıklığa yol açabilir.

Kancaların paylaşılması kolaydır, mantığı yeniden kullanmadan önce bileşenlerinizi değiştirmeniz gerekmez.

Buna güzel bir örnek, Redux mağazasına abone olmak için Redux connect Higher Order Component'in (HOC) kullanılmasıdır. Tüm HOC'lerde olduğu gibi, connect HOC'yi kullanmak için, bileşeni tanımlanmış üst düzey işlevlerle birlikte dışa aktarmanız gerekir. connect durumunda, bu formda bir şeye sahip olacağız.

 export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

mapStateToProps ve mapDispatchToProps tanımlanacak işlevlerdir.

Hooks döneminde ise Redux useSelector ve useDispatch kancalarını kullanarak aynı sonucu düzgün ve özlü bir şekilde kolayca elde edebilirsiniz.

Büyük Bileşenler

Sınıf bileşenleri genellikle yan etkiler ve durum bilgisi içeren mantık içerir. Uygulamanın karmaşıklığı arttıkça, bileşenin dağınık ve kafa karıştırıcı hale gelmesi yaygındır. Bunun nedeni, yan etkilerin işlevsellikten çok yaşam döngüsü yöntemleriyle organize edilmesinin beklenmesidir. Bileşenleri bölmek ve daha basit hale getirmek mümkün olsa da, bu genellikle daha yüksek düzeyde bir soyutlama sağlar.

Kancalar, yan etkileri işlevselliğe göre düzenler ve bir bileşeni işlevselliğe göre parçalara ayırmak mümkündür.

Kafa Karıştırıcı Sınıflar

Sınıflar genellikle işlevlerden daha zor bir kavramdır. React sınıfı tabanlı bileşenler ayrıntılıdır ve yeni başlayanlar için biraz zordur. Javascript'te yeniyseniz, sınıflara kıyasla hafif sözdizimleri nedeniyle başlangıç ​​için daha kolay işlevler bulabilirsiniz. Sözdizimi kafa karıştırıcı olabilir; bazen, kodu kırabilecek bir olay işleyicisinin bağlanmasını unutmak mümkündür.

React, bu sorunu işlevsel bileşenler ve kancalarla çözerek geliştiricilerin kod sözdizimi yerine projeye odaklanmasını sağlar.

Örneğin, aşağıdaki iki React bileşeni tamamen aynı sonucu verecektir.

 import React, { Component } from "react"; export default class App extends Component { constructor(props) { super(props); this.state = { num: 0 }; this.incrementNumber = this.incrementNumber.bind(this); } incrementNumber() { this.setState({ num: this.state.num + 1 }); } render() { return ( <div> <h1>{this.state.num}</h1> <button onClick={this.incrementNumber}>Increment</button> </div> ); } }
 import React, { useState } from "react"; export default function App() { const [num, setNum] = useState(0); function incrementNumber() { setNum(num + 1); } return ( <div> <h1>{num}</h1> <button onClick={incrementNumber}>Increment</button> </div> ); }

İlk örnek sınıf tabanlı bir bileşen, ikincisi ise işlevsel bir bileşendir. Bu basit bir örnek olmasına rağmen, ilk örneğin ikinci ile karşılaştırıldığında ne kadar sahte olduğuna dikkat edin.

Hooks Sözleşmesi ve Kuralları

Çeşitli kancaları incelemeden önce, bunlar için geçerli olan sözleşmeye ve kurallara bir göz atmak faydalı olabilir. İşte kancalar için geçerli olan kurallardan bazıları.

  1. Kancaların adlandırma kuralı, use önekiyle başlamalıdır. Böylece useState , useEffect , vs.'ye sahip olabiliriz. Atom ve VSCode gibi modern kod düzenleyicileri kullanıyorsanız, ESLint eklentisi React kancaları için çok kullanışlı bir özellik olabilir. Eklenti, en iyi uygulamalar hakkında faydalı uyarılar ve ipuçları sağlar.
  2. Hook'lar, return ifadesinden önce bir bileşenin en üst seviyesinde çağrılmalıdır. Koşullu bir ifade, döngü veya iç içe işlevler içinde çağrılamazlar.
  3. Kancalar bir React işlevinden çağrılmalıdır (bir React bileşeninin veya başka bir kancanın içinde). Vanilla JS işlevinden çağrılmamalıdır.

useState Kancası

useState kancası, en temel ve kullanışlı React kancasıdır. Diğer yerleşik kancalar gibi, bu kancanın da uygulamamızda kullanılabilmesi için react içe aktarılması gerekir.

 import {useState} from 'react'

Durumu başlatmak için hem durumu hem de güncelleyici işlevini bildirmeli ve bir başlangıç ​​değeri iletmeliyiz.

 const [state, updaterFn] = useState('')

Durumumuzu ve güncelleyici işlevimizi istediğimiz gibi çağırmakta özgürüz, ancak geleneksel olarak dizinin ilk öğesi durumumuz, ikinci öğe ise güncelleyici işlevi olacaktır. Güncelleyici fonksiyonumuzun önüne deve durumu formunda devletimizin adının geldiği önek seti ile eklemek yaygın bir uygulamadır.

Örneğin, sayım değerlerini tutacak bir durum belirleyelim.

 const [count, setCount] = useState(0)

count durumumuzun ilk değerinin boş bir dize değil 0 olarak ayarlandığına dikkat edin. Başka bir deyişle, durumumuzu sayı, dize, boolean, dizi, nesne ve hatta BigInt gibi herhangi bir JavaScript değişkenine başlatabiliriz. useState kancasıyla ayar durumları ve sınıf tabanlı bileşen durumları arasında açık bir fark vardır. useState kancasının, durum değişkenleri olarak da bilinen bir dizi döndürmesi dikkat çekicidir ve yukarıdaki örnekte diziyi state ve updater işlevine dönüştürdük.

Yeniden Oluşturma Bileşenleri

useState kancasıyla durumları ayarlamak, karşılık gelen bileşenin yeniden oluşturulmasına neden olur. Ancak bu, yalnızca React önceki veya eski durum ile yeni durum arasında bir fark tespit ederse gerçekleşir. React, Javascript Object.is algoritmasını kullanarak durum karşılaştırmasını yapar.

useState ile Durumları Ayarlama

count durumumuz, yeni değeri aşağıdaki gibi setCount updater işlevine geçirerek yeni durum değerlerine ayarlanabilir setCount(newValue) .

Bu yöntem, önceki durum değerine başvurmak istemediğimizde çalışır. Bunu yapmak istiyorsak, setCount işlevine bir işlev iletmemiz gerekir.

Bir butona her tıklandığında count değişkenimize 5 eklemek istediğimizi varsayarsak, aşağıdakileri yapabiliriz.

 import {useState} from 'react' const CountExample = () => { // initialize our count state const [count, setCount] = useState(0) // add 5 to to the count previous state const handleClick = () =>{ setCount(prevCount => prevCount + 5) } return( <div> <h1>{count} </h1> <button onClick={handleClick}>Add Five</button> </div> ) } export default CountExample

Yukarıdaki kodda, ilk olarak useState kancasını içe react ve ardından count durumunu varsayılan 0 değeriyle başlattık. Düğmeye her tıklandığında count değerini 5 artırmak için bir onClick işleyicisi oluşturduk. Sonra sonucu bir h1 etiketinde gösterdik.

Dizileri ve Nesne Durumlarını Ayarlama

Diziler ve nesneler için durumlar, diğer veri türleriyle hemen hemen aynı şekilde ayarlanabilir. Ancak, mevcut değerleri korumak istiyorsak, durumları ayarlarken ES6 yayılma operatörünü kullanmamız gerekir.

Javascript'teki yayılma operatörü, zaten var olan bir nesneden yeni bir nesne oluşturmak için kullanılır. Bu, burada yararlıdır çünkü React , durumları Object.is işlemiyle karşılaştırır ve ardından buna göre yeniden oluşturur.

Düğme tıklandığında durumları ayarlamak için aşağıdaki kodu ele alalım.

 import {useState} from 'react' const StateExample = () => { //initialize our array and object states const [arr, setArr] = useState([2, 4]) const [obj, setObj] = useState({num: 1, name: 'Desmond'}) // set arr to the new array values const handleArrClick = () =>{ const newArr = [1, 5, 7] setArr([...arr, ...newArr]) } // set obj to the new object values const handleObjClick = () =>{ const newObj = {name: 'Ifeanyi', age: 25} setObj({...obj, ...newObj}) } return( <div> <button onClick ={handleArrClick}>Set Array State</button> <button onClick ={handleObjClick}>Set Object State</button> </div> ) } export default StateExample

Yukarıdaki kodda, arr ve obj olmak üzere iki durum oluşturduk ve bunları sırasıyla bazı dizi ve nesne değerlerine başlattık. Ardından, sırasıyla dizinin ve nesnenin durumlarını ayarlamak için handleArrClick ve handleObjClick adlı onClick işleyicileri oluşturduk. handleArrClick setArr ve zaten var olan dizi değerlerini yaymak ve ona newArr eklemek için ES6 spread operatörünü kullanırız.

Aynı şeyi handleObjClick işleyicisi için de yaptık. Burada setObj adını verdik, mevcut nesne değerlerini ES6 yayılma operatörünü kullanarak yaydık ve name ve age değerlerini güncelledik.

useState Async Doğası

Daha önce gördüğümüz gibi, updater fonksiyonuna yeni bir değer ileterek useState ile durumları belirledik. Güncelleyici birden çok kez çağrılırsa, yeni değerler bir kuyruğa eklenir ve JavaScript Object.is karşılaştırması kullanılarak buna göre yeniden oluşturma yapılır.

Durumlar eşzamansız olarak güncellenir. Bu, yeni durumun önce bir bekleme durumuna eklendiği ve ardından durumun güncellendiği anlamına gelir. Böylece, ayarlandığı hemen duruma erişirseniz, eski durum değerini almaya devam edebilirsiniz.

Bu davranışı gözlemlemek için aşağıdaki örneği ele alalım.

Yukarıdaki kodda useState kancasını kullanarak bir count durumu oluşturduk. Ardından, düğmeye her tıklandığında count durumunu artırmak için bir onClick işleyicisi oluşturduk. h2 etiketinde gösterildiği gibi, count durumunun artmasına rağmen, önceki durumun konsolda günlüğe kaydedildiğini gözlemleyin. Bu, kancanın zaman uyumsuz doğasından kaynaklanmaktadır.

Eğer yeni durumu elde etmek istiyorsak, bunu asenkron fonksiyonlarda yaptığımız gibi halledebiliriz. İşte bunu yapmanın bir yolu.

Burada, güncellenen sayım değerini saklamak için oluşturulan newCountValue depoladık ve ardından count durumunu güncellenen değerle ayarladık. Ardından güncellenen sayım değerini konsola kaydettik.

Kullanım useEffect Kancası

useEffect , çoğu projede kullanılan bir diğer önemli React kancasıdır. Sınıf tabanlı bileşenin componentDidMount , componentWillUnmount ve componentDidUpdate yaşam döngüsü yöntemlerine benzer bir şey yapar. useEffect bize uygulama üzerinde yan etkileri olabilecek zorunlu kodlar yazmamıza olanak sağlıyor. Bu tür etkilerin örnekleri arasında günlüğe kaydetme, abonelikler, mutasyonlar vb.

Kullanıcı, useEffect ne zaman çalışacağına karar verebilir, ancak ayarlanmazsa, yan etkiler her oluşturma veya yeniden oluşturma işleminde çalışır.

Aşağıdaki örneği düşünün.

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ console.log(count) }) return( <div> ... </div> ) }

Yukarıdaki kodda, useEffect sadece count kaydettik. Bu, bileşenin her oluşturulmasından sonra çalışacaktır.

Bazen, bileşenimizde kancayı bir kez (montajda) çalıştırmak isteyebiliriz. Bunu, useEffect kancasına ikinci bir parametre sağlayarak başarabiliriz.

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ setCount(count + 1) }, []) return( <div> <h1>{count}</h1> ... </div> ) }

useEffect kancasının iki parametresi vardır, ilk parametre çalıştırmak istediğimiz fonksiyondur, ikinci parametre ise bir dizi bağımlılıktır. İkinci parametre sağlanmazsa, kanca sürekli çalışacaktır.

Kancanın ikinci parametresine boş bir köşeli ayraç ileterek, useEffect kancasını yuva üzerinde yalnızca bir kez çalıştırmasını söyleriz. Bileşen bağlandığında sayı 0'dan 1'e bir kez güncelleneceğinden, bu h1 etiketinde 1 değerini gösterecektir.

Bazı bağımlı değerler değiştiğinde yan etkimizi de çalıştırabiliriz. Bu, bu değerleri bağımlılıklar listesinde geçirerek yapılabilir.

Örneğin, aşağıdaki gibi count değiştiğinde useEffect çalıştırabiliriz.

 import { useState, useEffect } from "react"; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(count); }, [count]); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default App;

Yukarıdaki useEffect , bu iki koşuldan biri karşılandığında çalışacaktır.

  1. Montajda — bileşen oluşturulduktan sonra.
  2. count değeri değiştiğinde.

Bağlamada, console.log ifadesi çalışacak ve count 0'a günlüğe kaydedecektir. count güncellendiğinde, ikinci koşul karşılanır, bu nedenle useEffect yeniden çalışır, düğme her tıklandığında bu devam eder.

useEffect için ikinci argümanı sağladığımızda, tüm bağımlılıkları ona aktarmamız beklenir. ESLINT kuruluysa, parametre listesine herhangi bir bağımlılık geçirilmediyse tiftik hatası gösterecektir. Bu ayrıca, özellikle aktarılmayan parametrelere bağlıysa, yan etkinin beklenmedik şekilde davranmasına neden olabilir.

Efekti Temizlemek

useEffect ayrıca, bileşenin bağlantısını kesmeden önce kaynakları temizlememize de olanak tanır. Bu, bellek sızıntılarını önlemek ve uygulamayı daha verimli hale getirmek için gerekli olabilir. Bunu yapmak için, kancanın sonundaki temizleme işlevini döndürürüz.

 useEffect(() => { console.log('mounted') return () => console.log('unmounting... clean up here') })

Yukarıdaki useEffect kancası, bileşen mounted edildiğinde takılı günlüğe kaydedilir. Unmounting… buradaki temizleme , bileşen bağlantısı kesildiğinde günlüğe kaydedilir. Bu, bileşen kullanıcı arayüzünden kaldırıldığında gerçekleşebilir.

Temizleme işlemi tipik olarak aşağıdaki formu takip eder.

 useEffect(() => { //The effect we intend to make effect //We then return the clean up return () => the cleanup/unsubscription })

useEffect abonelikleri için çok fazla kullanım örneği bulamasanız da, abonelikler ve zamanlayıcılarla uğraşırken kullanışlıdır. Özellikle, web soketleriyle uğraşırken, kaynaktan tasarruf etmek ve bileşen ayrıldığında performansı artırmak için ağ aboneliğinden çıkmanız gerekebilir.

useEffect ile Verileri Alma ve Yeniden Getirme

useEffect kancasının en yaygın kullanım durumlarından biri, bir API'den veri almak ve önceden getirmektir.

Bunu göstermek için, useEffect kancasıyla verileri almak için JSONPlaceholder oluşturduğum sahte kullanıcı verilerini kullanacağız.

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [users, setUsers] = useState([]); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/users"; useEffect(() => { const fetchUsers = async () => { const { data } = await axios.get(endPoint); setUsers(data); }; fetchUsers(); }, []); return ( <div className="App"> {users.map((user) => ( <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> ))} </div> ); }

Yukarıdaki kodda useState kancasını kullanarak bir users durumu oluşturduk. Daha sonra Axios kullanarak bir API'den veri aldık. Bu eşzamansız bir işlemdir ve bu nedenle eşzamansız/bekleme işlevini kullandık, noktayı sonra sözdizimini de kullanabilirdik. Bir kullanıcı listesi getirdiğimizden, verileri görüntülemek için basitçe haritayı çıkardık.

Kancaya boş bir parametre ilettiğimize dikkat edin. Bu, bileşen monte edildiğinde yalnızca bir kez çağrılmasını sağlar.

Ayrıca bazı koşullar değiştiğinde verileri yeniden getirebiliriz. Bunu aşağıdaki kodda göstereceğiz.

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [userIDs, setUserIDs] = useState([]); const [user, setUser] = useState({}); const [currentID, setCurrentID] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/userdata/users"; useEffect(() => { axios.get(endPoint).then(({ data }) => setUserIDs(data)); }, []); useEffect(() => { const fetchUserIDs = async () => { const { data } = await axios.get(`${endPoint}/${currentID}`}); setUser(data); }; fetchUserIDs(); }, [currentID]); const moveToNextUser = () => { setCurrentID((prevId) => (prevId < userIDs.length ? prevId + 1 : prevId)); }; const moveToPrevUser = () => { setCurrentID((prevId) => (prevId === 1 ? prevId : prevId - 1)); }; return ( <div className="App"> <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> <button onClick={moveToPrevUser}>Prev</button> <button onClick={moveToNextUser}>Next</button> </div> ); }

Burada iki useEffect kancası oluşturduk. İlkinde, tüm kullanıcıları API'mızdan almak için nokta sonra sözdizimini kullandık. Bu, kullanıcı sayısını belirlemek için gereklidir.

Daha sonra, id dayalı bir kullanıcı elde etmek için başka bir useEffect kancası oluşturduk. Bu useEffect , kimlik değiştiğinde verileri yeniden getirecektir. Bunu sağlamak için bağımlılık listesinde id geçtik.

Daha sonra, butonlara her tıklandığında id değerini güncellemek için fonksiyonlar oluşturduk. useEffect id çalışacak ve verileri yeniden getirecektir.

İstersek, Axios'ta söze dayalı belirteci temizleyebilir veya iptal edebiliriz, bunu yukarıda tartışılan temizleme yöntemiyle yapabiliriz.

 useEffect(() => { const source = axios.CancelToken.source(); const fetchUsers = async () => { const { data } = await axios.get(`${endPoint}/${num}`, { cancelToken: source.token }); setUser(data); }; fetchUsers(); return () => source.cancel(); }, [num]);

Burada, Axios'un belirtecini axios.get ikinci bir parametre olarak ilettik. Bileşen bağlantısı kaldırıldığında, kaynak nesnenin iptal yöntemini çağırarak aboneliği iptal ettik.

Kullanım useReducer Kancası

useReducer kancası, useState kancasına benzer bir şey yapan çok kullanışlı bir React kancasıdır. React belgelerine göre, bu kanca, useState kancasından daha karmaşık mantığı işlemek için kullanılmalıdır. useState kancasıyla uygulandığını belirtmekte fayda var.

Kanca argüman olarak bir indirgeyici alır ve isteğe bağlı olarak ilk durumu ve bir init fonksiyonunu argüman olarak alabilir.

 const [state, dispatch] = useReducer(reducer, initialState, init)

Burada init bir fonksiyondur ve ilk durumu tembelce oluşturmak istediğimizde kullanılır.

Aşağıdaki sanal alanda gösterildiği gibi basit bir yapılacaklar uygulaması oluşturarak useReducer kancasının nasıl uygulanacağına bakalım.

Yapılacaklar Örneği

Öncelikle durumları tutacak redüktörümüzü oluşturmalıyız.

 export const ADD_TODO = "ADD_TODO"; export const REMOVE_TODO = "REMOVE_TODO"; export const COMPLETE_TODO = "COMPLETE_TODO"; const reducer = (state, action) => { switch (action.type) { case ADD_TODO: const newTodo = { id: action.id, text: action.text, completed: false }; return [...state, newTodo]; case REMOVE_TODO: return state.filter((todo) => todo.id !== action.id); case COMPLETE_TODO: const completeTodo = state.map((todo) => { if (todo.id === action.id) { return { ...todo, completed: !todo.completed }; } else { return todo; } }); return completeTodo; default: return state; } }; export default reducer;

Eylem türlerimize karşılık gelen üç sabit oluşturduk. Dizeleri doğrudan kullanabilirdik, ancak yazım hatalarını önlemek için bu yöntem tercih edilir.

Daha sonra redüktör fonksiyonumuzu oluşturduk. Redux olduğu gibi, redüktörün durumu ve eylem nesnesini alması gerekir. Ancak Redux'un aksine burada redüktörümüzü başlatmamız gerekmiyor.

Ayrıca, birçok durum yönetimi kullanım senaryosu için, bağlam aracılığıyla sunulan dispatch birlikte bir useReducer , daha büyük bir uygulamanın eylemleri başlatmasını, state güncellemesini ve dinlemesini sağlayabilir.

Ardından, kullanıcı tarafından iletilen eylem türünü kontrol etmek için switch deyimlerini kullandık. Eylem türü ADD_TODO ise yeni bir yapılacak iş geçmek ve REMOVE_TODO ise yapılacakları filtrelemek ve kullanıcı tarafından iletilen id karşılık gelen öğeyi kaldırmak istiyoruz. COMPLETE_TODO ise, yapılacaklar arasında eşlemek ve kullanıcı tarafından geçen id sahip olanı değiştirmek istiyoruz.

İşte reducer uyguladığımız App.js dosyası.

 import { useReducer, useState } from "react"; import "./styles.css"; import reducer, { ADD_TODO, REMOVE_TODO, COMPLETE_TODO } from "./reducer"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = [ { id: id, text: "First Item", completed: false } ]; //We could also pass an empty array as the initial state //const initialState = [] const [state, dispatch] = useReducer(reducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); dispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="App"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit">+</button> </form> <div className="todos"> {state.map((todo) => ( <div key={todo.id} className="todoItem"> <p className={todo.completed && "strikethrough"}>{todo.text}</p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ))} </div> </div> ); }

Burada, kullanıcının girdisini toplamak için bir girdi öğesi ve eylemi tetiklemek için bir düğme içeren bir form oluşturduk. Form gönderildiğinde, yeni bir kimlik ve yapılacaklar metni ileterek ADD_TODO türünde bir eylem gönderdik. Önceki id değerini 1 artırarak yeni bir id oluşturduk. Ardından giriş metin kutusunu temizledik. Yapılacakları silmek ve tamamlamak için uygun eylemleri göndermemiz yeterlidir. Bunlar, yukarıda gösterildiği gibi redüktörde zaten uygulanmıştır.

Ancak, sihir, useReducer kancasını kullandığımız için gerçekleşir. Bu kanca, indirgeyiciyi ve başlangıç ​​durumunu kabul eder ve durumu ve gönderme işlevini döndürür. Burada, sevk işlevi, useState kancası için ayarlayıcı işleviyle aynı amaca hizmet eder ve onu dispatch yerine istediğimiz herhangi bir şey olarak adlandırabiliriz.

Yapılacakları görüntülemek için, yukarıdaki kodda gösterildiği gibi durum nesnemizde döndürülen yapılacaklar listesinin haritasını çıkardık.

Bu, useReducer kancasının gücünü gösterir. Bu işlevi useState kancasıyla da elde edebiliriz, ancak yukarıdaki örnekten de görebileceğiniz gibi, useReducer kancası işleri daha düzenli tutmamıza yardımcı oldu. useReducer , durum nesnesi karmaşık bir yapı olduğunda ve basit bir değer değiştirmeye göre farklı şekillerde güncellendiğinde genellikle faydalıdır. Ayrıca, bu güncelleme işlevleri daha karmaşık hale geldiğinde, useReducer , tüm bu karmaşıklığı bir redüktör işlevinde (saf bir JS işlevidir) tutmayı kolaylaştırır, bu da yalnızca redüktör işlevi için testler yazmayı çok kolaylaştırır.

İlk durumu tembelce oluşturmak için üçüncü argümanı useReducer kancasına da iletebilirdik. Bu, bir init işlevinde ilk durumu hesaplayabileceğimiz anlamına gelir.

Örneğin, aşağıdaki gibi bir init işlevi oluşturabiliriz:

 const initFunc = () => [ { id: id, text: "First Item", completed: false } ]

ve sonra bunu useReducer .

 const [state, dispatch] = useReducer(reducer, initialState, initFunc)

Bunu yaparsak, initFunc sağladığımız initialState geçersiz kılacak ve başlangıç ​​durumu tembelce hesaplanacaktır.

useContext Hook

React Context API, React bileşen ağacı boyunca durumları veya verileri paylaşmanın bir yolunu sağlar. API, bir süredir deneysel bir özellik olarak React'te mevcuttu ancak React 16.3.0'da kullanımı güvenli hale geldi. API, pervane delme işlemini ortadan kaldırırken bileşenler arasında veri paylaşımını kolaylaştırır.

React Context'i uygulamanızın tamamına uygulayabileceğiniz gibi, uygulamanın bir kısmına da uygulamanız mümkündür.

Kancayı kullanmak için önce React.createContext kullanarak bir bağlam oluşturmanız gerekir ve bu bağlam daha sonra kancaya geçirilebilir.

useContext kancasının kullanımını göstermek için, uygulamamız boyunca yazı tipi boyutunu artıracak basit bir uygulama oluşturalım.

context.js dosyasında oluşturalım.

 import { createContext } from "react"; //Here, we set the initial fontSize as 16. const fontSizeContext = createContext(16); export default fontSizeContext;

Burada bir bağlam oluşturduk ve ona 16 bir başlangıç ​​değeri ilettik ve ardından bağlamı dışa aktardık. Ardından, bağlamımızı uygulamamıza bağlayalım.

 import FontSizeContext from "./context"; import { useState } from "react"; import PageOne from "./PageOne"; import PageTwo from "./PageTwo"; const App = () => { const [size, setSize] = useState(16); return ( <FontSizeContext.Provider value={size}> <PageOne /> <PageTwo /> <button onClick={() => setSize(size + 5)}>Increase font</button> <button onClick={() => setSize((prevSize) => Math.min(11, prevSize - 5)) } > Decrease font </button> </FontSizeContext.Provider> ); }; export default App;

Yukarıdaki kodda, tüm bileşen ağacımızı FontSizeContext.Provider ile FontSizeContext.Provider ve size değerini prop değerine ilettik. Burada size , useState kancasıyla oluşturulan bir durumdur. Bu, size durumu değiştiğinde prop değerini değiştirmemize izin verir. Tüm bileşeni Provider ile sararak, içeriğe uygulamamızın herhangi bir yerinden erişebiliriz.

Örneğin, <PageOne /> ve <PageTwo /> içindeki içeriğe eriştik. Bunun bir sonucu olarak, App.js dosyasından artırdığımızda yazı tipi boyutu bu iki bileşen arasında artacaktır. Yazı boyutunu yukarıda gösterildiği gibi butonlardan büyütüp küçültebiliyoruz ve yaptığımızda yazı boyutu uygulama genelinde değişiyor.

 import { useContext } from "react"; import context from "./context"; const PageOne = () => { const size = useContext(context); return <p style={{ fontSize: `${size}px` }}>Content from the first page</p>; }; export default PageOne;

Burada, PageOne bileşenimizdeki useContext kancasını kullanarak içeriğe eriştik. Daha sonra yazı tipi boyutu özelliğimizi ayarlamak için bu bağlamı kullandık. Benzer bir prosedür PageTwo.js dosyası için de geçerlidir.

Temalar veya diğer üst düzey uygulama düzeyinde yapılandırmalar, bağlamlar için iyi adaylardır.

useContext ve useReducer kullanma

useReducer kancasıyla birlikte kullanıldığında useContext , kendi durum yönetim sistemimizi oluşturmamıza olanak tanır. Uygulamamızda küresel devletler oluşturabilir ve bunları kolayca yönetebiliriz.

Bağlam API'sini kullanarak yapılacaklar uygulamamızı geliştirelim.

Her zamanki gibi, todoContext.js dosyasında bir todoContext oluşturmamız gerekiyor.

 import { createContext } from "react"; const initialState = []; export default createContext(initialState);

Burada, boş bir dizinin başlangıç ​​değerini ileterek bağlamı oluşturduk. Sonra bağlamı dışa aktardık.

Yapılacaklar listesini ve öğeleri ayırarak App.js dosyamızı yeniden düzenleyelim.

 import { useReducer, useState } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import TodoList from "./TodoList"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <div className="app"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </TodoContext.Provider> ); }

Burada, App.js dosyamızı TodoContext.Provider ile TodoContext.Provider ve ardından todoReducer'ımızın dönüş değerlerini ona todoReducer . Bu, redüktörün durumu ve dispatch fonksiyonunun uygulamamız boyunca erişilebilir olmasını sağlar.

Daha sonra yapılacaklar ekranını TodoList bileşenine ayırdık. Bunu, Context API sayesinde sondaj yapmadan gerçekleştirdik. TodoList.js dosyasına bir göz atalım.

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import Todo from "./Todo"; const TodoList = () => { const [state] = useContext(TodoContext); return ( <div className="todos"> {state.map((todo) => ( <Todo key={todo.id} todo={todo} /> ))} </div> ); }; export default TodoList;

Dizi yok etmeyi kullanarak, useContext kancasını kullanarak bağlamdan duruma (gönderme işlevinden çıkarak) erişebiliriz. Daha sonra durumu haritalayabilir ve yapılacaklar öğelerini görüntüleyebiliriz. Bunu yine de bir Todo bileşeninde çıkardık. ES6+ harita işlevi, benzersiz bir anahtar iletmemizi gerektirir ve belirli bir yapılacaklığa ihtiyacımız olduğundan, onu da yanında iletiyoruz.

Şimdi Todo bileşenine bir göz atalım.

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import { REMOVE_TODO, COMPLETE_TODO } from "./todoReducer"; const Todo = ({ todo }) => { const [, dispatch] = useContext(TodoContext); const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="todoItem"> <p className={todo.completed ? "strikethrough" : "nostrikes"}> {todo.text} </p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ); }; export default Todo;

Yine dizi yok etmeyi kullanarak, bağlamdan sevk işlevine eriştik. Bu, useReducer bölümünde daha önce tartışıldığı gibi completeTodo ve removeTodo işlevini tanımlamamızı sağlar. todoList.js geçirilen todo prop ile bir yapılacaklar öğesini görüntüleyebiliriz. Ayrıca tamamlandı olarak işaretleyebilir ve uygun gördüğümüz yapılacakları kaldırabiliriz.

Uygulamamızın köküne birden fazla bağlam sağlayıcı yerleştirmek de mümkündür. Bu, bir uygulamada farklı işlevleri gerçekleştirmek için birden fazla bağlam kullanabileceğimiz anlamına gelir.

Bunu göstermek için, yapılacaklar örneğine tema ekleyelim.

İşte inşa edeceğimiz şey.

Yine themeContext oluşturmamız gerekiyor. Bunu yapmak için bir themeContext.js dosyası oluşturun ve aşağıdaki kodları ekleyin.

 import { createContext } from "react"; import colors from "./colors"; export default createContext(colors.light);

Burada bir bağlam oluşturduk ve ilk değer olarak colors.light geçtik. Renkleri bu özellik ile colors.js dosyasında tanımlayalım.

 const colors = { light: { backgroundColor: "#fff", color: "#000" }, dark: { backgroundColor: "#000", color: "#fff" } }; export default colors;

Yukarıdaki kodda, açık ve koyu özelliklerini içeren bir colors nesnesi oluşturduk. Her özelliğin backgroundColor ve color nesnesi vardır.

Ardından, tema durumlarını işlemek için themeReducer oluşturuyoruz.

 import Colors from "./colors"; export const LIGHT = "LIGHT"; export const DARK = "DARK"; const themeReducer = (state, action) => { switch (action.type) { case LIGHT: return { ...Colors.light }; case DARK: return { ...Colors.dark }; default: return state; } }; export default themeReducer;

Tüm redüktörler gibi, themeReducer durumu ve eylemi alır. Ardından, geçerli eylemi belirlemek için switch ifadesini kullanır. LIGHT türündeyse, Colors.light atarız ve DARK Colors.dark görüntüleriz. Bunu useState kancasıyla kolayca yapabilirdik, ancak noktayı eve götürmek için useReducer .

themeReducer sonra onu App.js dosyamıza entegre edebiliriz.

 import { useReducer, useState, useCallback } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import ThemeContext from "./themeContext"; import TodoList from "./TodoList"; import themeReducer, { DARK, LIGHT } from "./themeReducer"; import Colors from "./colors"; import ThemeToggler from "./ThemeToggler"; const themeSetter = useCallback( theme => themeDispatch({type: theme}, [themeDispatch]); export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const [themeState, themeDispatch] = useReducer(themeReducer, Colors.light); const themeSetter = useCallback( (theme) => { themeDispatch({ type: theme }); }, [themeDispatch] ); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <ThemeContext.Provider value={[ themeState, themeSetter ]} > <div className="app" style={{ ...themeState }}> <ThemeToggler /> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </ThemeContext.Provider> </TodoContext.Provider> ); }

Yukarıdaki kodda, zaten var olan yapılacaklar uygulamamıza birkaç şey ekledik. ThemeContext , themeReducer , ThemeToggler ve Colors öğelerini içe aktararak başladık. We created a reducer using the useReducer hook, passing the themeReducer and an initial value of Colors.light to it. This returned the themeState and themeDispatch to us.

We then nested our component with the provider function from the ThemeContext , passing the themeState and the dispatch functions to it. We also added theme styles to it by spreading out the themeStates . This works because the colors object already defined properties similar to what the JSX styles will accept.

However, the actual theme toggling happens in the ThemeToggler component. Bir göz atalım.

 import ThemeContext from "./themeContext"; import { useContext, useState } from "react"; import { DARK, LIGHT } from "./themeReducer"; const ThemeToggler = () => { const [showLight, setShowLight] = useState(true); const [themeState, themeSetter] = useContext(ThemeContext); const dispatchDarkTheme = () => themeSetter(DARK); const dispatchLightTheme = () => themeSetter(LIGHT); const toggleTheme = () => { showLight ? dispatchDarkTheme() : dispatchLightTheme(); setShowLight(!showLight); }; console.log(themeState); return ( <div> <button onClick={toggleTheme}> {showLight ? "Change to Dark Theme" : "Change to Light Theme"} </button> </div> ); }; export default ThemeToggler;

In this component, we used the useContext hook to retrieve the values we passed to the ThemeContext.Provider from our App.js file. As shown above, these values include the ThemeState , dispatch function for the light theme, and dispatch function for the dark theme. Thereafter, we simply called the dispatch functions to toggle the themes. We also created a state showLight to determine the current theme. This allows us to easily change the button text depending on the current theme.

The useMemo Hook

The useMemo hook is designed to memoize expensive computations. Memoization simply means caching. It caches the computation result with respect to the dependency values so that when the same values are passed, useMemo will just spit out the already computed value without recomputing it again. This can significantly improve performance when done correctly.

The hook can be used as follows:

 const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])

Let's consider three cases of the useMemo hook.

  1. When the dependency values, a and b remain the same.
    The useMemo hook will return the already computed memoized value without recomputation.
  2. When the dependency values, a and b change.
    The hook will recompute the value.
  3. When no dependency value is passed.
    The hook will recompute the value.

Let's take a look at an example to demonstrate this concept.

In the example below, we'll be computing the PAYE and Income after PAYE of a company's employees with fake data from JSONPlaceholder.

The calculation will be based on the personal income tax calculation procedure for Nigeria providers by PricewaterhouseCoopers available here.

This is shown in the sandbox below.

First, we queried the API to get the employees' data. We also get data for each employee (with respect to their employee id).

const [employee, setEmployee] = useState({}); const [employees, setEmployees] = useState([]); const [num, setNum] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; useEffect(() => { const getEmployee = async () => { const { data } = await axios.get(`${endPoint}/${num}`); setEmployee(data); }; getEmployee(); }, [num]); useEffect(() => { axios.get(endPoint).then(({ data }) => setEmployees(data)); }, [num]);

İlk useEffect axios ve async/await yöntemini, ikincide nokta ve ardından sözdizimini kullandık. Bu iki yaklaşım aynı şekilde çalışır.

Ardından, yukarıdan aldığımız çalışan verilerini kullanarak kabartma değişkenlerini hesaplayalım:

 const taxVariablesCompute = useMemo(() => { const { income, noOfChildren, noOfDependentRelatives } = employee; //supposedly complex calculation //tax relief computations for relief Allowance, children relief, // relatives relief and pension relief const reliefs = reliefAllowance1 + reliefAllowance2 + childrenRelief + relativesRelief + pensionRelief; return reliefs; }, [employee]);

Bu oldukça karmaşık bir hesaplamadır ve bu yüzden onu not almak veya optimize etmek için bir useMemo kancasına sarmamız gerekiyordu. Bu şekilde hafızaya almak, aynı çalışana tekrar erişmeye çalışırsak hesaplamanın yeniden hesaplanmamasını sağlayacaktır.

Ayrıca, yukarıda elde edilen vergi indirimi değerlerini kullanarak, PAYE'yi ve PAYE'den sonraki geliri hesaplamak istiyoruz.

 const taxCalculation = useMemo(() => { const { income } = employee; let taxableIncome = income - taxVariablesCompute; let PAYE = 0; //supposedly complex calculation //computation to compute the PAYE based on the taxable income and tax endpoints const netIncome = income - PAYE; return { PAYE, netIncome }; }, [employee, taxVariablesCompute]);

Yukarıda hesaplanan vergi değişkenlerini kullanarak vergi hesaplaması (oldukça karmaşık bir hesaplama) gerçekleştirdik ve ardından bunu useMemo kancasıyla not aldık.

Kodun tamamı burada mevcuttur.

Bu, burada verilen vergi hesaplama prosedürünü takip eder. Vergi indirimini önce gelir, çocuk sayısı ve bağımlı akraba sayısı üzerinden hesapladık. Daha sonra vergilendirilebilir geliri PIT oranları ile basamaklar halinde çarpıyoruz. Söz konusu hesaplama bu eğitim için tamamen gerekli olmasa da, useMemo neden gerekli olabileceğini bize göstermek için sağlanmıştır. Bu aynı zamanda oldukça karmaşık bir hesaplamadır ve bu nedenle yukarıda gösterildiği gibi useMemo ile ezberlememiz gerekebilir.

Değerleri hesapladıktan sonra sonucu gösterdik.

useMemo kancası hakkında aşağıdakilere dikkat edin.

  • useMemo yalnızca hesaplamayı optimize etmek gerektiğinde kullanılmalıdır. Başka bir deyişle, yeniden hesaplama pahalı olduğunda.
  • İlk önce hesaplamayı ezberlemeden yazmanız ve yalnızca performans sorunlarına neden oluyorsa ezberlemeniz önerilir.
  • useMemo kancasının gereksiz ve alakasız kullanımı performans sorunlarını daha da karmaşık hale getirebilir.
  • Bazen çok fazla not alma da performans sorunlarına neden olabilir.

Kullanım Geri useCallback Kancası

useCallback , useMemo ile aynı amaca hizmet eder, ancak not edilmiş bir değer yerine not edilmiş bir geri arama döndürür. Başka bir deyişle, useCallback , bir işlev çağrısı olmadan useMemo aynıdır.

Örneğin, aşağıdaki kodları inceleyin.

 import React, {useCallback, useMemo} from 'react' const MemoizationExample = () => { const a = 5 const b = 7 const memoResult = useMemo(() => a + b, [a, b]) const callbackResult = useCallback(a + b, [a, b]) console.log(memoResult) console.log(callbackResult) return( <div> ... </div> ) } export default MemoizationExample

Yukarıdaki örnekte, hem memoResult hem de callbackResult , aynı 12 değerini verecektir. Burada useCallback , not edilmiş bir değer döndürür. Bununla birlikte, onu bir fonksiyon olarak ileterek, not edilmiş bir geri arama döndürmesini de sağlayabiliriz.

Aşağıdaki useCallback , not alınmış bir geri arama döndürür.

 ... const callbackResult = useCallback(() => a + b, [a, b]) ...

Ardından, bir eylem gerçekleştirildiğinde veya bir useEffect kancasında geri aramayı tetikleyebiliriz.

 import {useCallback, useEffect} from 'react' const memoizationExample = () => { const a = 5 const b = 7 const callbackResult = useCallback(() => a + b, [a, b]) useEffect(() => { const callback = callbackResult() console.log(callback) }) return ( <div> <button onClick= {() => console.log(callbackResult())}> Trigger Callback </button> </div> ) } export default memoizationExample

Yukarıdaki kodda useCallback kancasını kullanarak bir geri çağırma işlevi tanımladık. Daha sonra bileşen monte edildiğinde ve ayrıca bir düğmeye tıklandığında useEffect kancasında geri aramayı çağırdık.

Hem useEffect hem de düğme tıklaması aynı sonucu verir.

useMemo kancası için geçerli olan kavramların, yapılması ve yapılmaması gerekenlerin useMemo kancası için de geçerli olduğunu useCallback . useMemo örneğini useCallback ile yeniden oluşturabiliriz.

useRef

useRef , bir uygulamada kalıcı olabilen bir nesne döndürür. Kancanın yalnızca bir özelliği vardır: current ve ona kolayca bir argüman iletebiliriz.

Sınıf tabanlı bileşenlerde kullanılan createRef ile aynı amaca hizmet eder. Bu kanca ile aşağıdaki gibi bir referans oluşturabiliriz:

 const newRef = useRef('')

Burada newRef adında yeni bir ref oluşturduk ve ona boş bir dize ilettik.

Bu kanca esas olarak iki amaç için kullanılır:

  1. DOM'a erişme veya DOM'u değiştirme ve
  2. Değişken durumları saklamak — bu, bir değer değiştiğinde bileşenin yeniden oluşturulmasını istemediğimizde kullanışlıdır.

DOM'yi manipüle etme

Bir DOM öğesine iletildiğinde, ref nesnesi o öğeye işaret eder ve DOM özniteliklerine ve özelliklerine erişmek için kullanılabilir.

İşte bu kavramı göstermek için çok basit bir örnek.

 import React, {useRef, useEffect} from 'react' const RefExample = () => { const headingRef = useRef('') console.log(headingRef) return( <div> <h1 className='topheading' ref={headingRef}>This is a h1 element</h1> </div> ) } export default RefExample

Yukarıdaki örnekte, boş bir dize geçen useRef kancasını kullanarak headingRef tanımladık. Daha sonra ref = {headingRef} ileterek h1 etiketindeki ref'i ayarladık. Bu referansı ayarlayarak, headingRef h1 elemanımızı işaret etmesini istedik. Bu, h1 elemanımızın özelliklerine referanstan erişebileceğimiz anlamına gelir.

Bunu görmek için, console.log(headingRef) değerini kontrol edersek, {current: HTMLHeadingElement} veya {current: h1} alırız ve öğenin tüm özelliklerini veya niteliklerini değerlendirebiliriz. Benzer bir şey, diğer herhangi bir HTML öğesi için de geçerlidir.

Örneğin, bileşen bağlandığında metni italik yapabiliriz.

 useEffect(() => { headingRef.current.style.font; }, []);

Metni başka bir şeye bile değiştirebiliriz.

 ... headingRef.current.innerHTML = "A Changed H1 Element"; ...

Hatta ana konteynerin arka plan rengini de değiştirebiliriz.

 ... headingRef.current.parentNode.style.backgroundColor = "red"; ...

Her türlü DOM manipülasyonu burada yapılabilir. headingRef.current document.querySelector('.topheading') ile aynı şekilde okunabileceğini gözlemleyin.

DOM öğesinin işlenmesinde useRef kancasının ilginç bir kullanım durumu, imleci giriş öğesine odaklamaktır. Hızlıca üzerinden geçelim.

 import {useRef, useEffect} from 'react' const inputRefExample = () => { const inputRef = useRef(null) useEffect(() => { inputRef.current.focus() }, []) return( <div> <input ref={inputRef} /> <button onClick = {() => inputRef.current.focus()}>Focus on Input </button> </div> ) } export default inputRefExample

Yukarıdaki kodda, useRef kancasını kullanarak inputRef yarattık ve ardından girdi öğesini işaret etmesini istedik. Ardından, bileşen yüklendiğinde ve inputRef.current.focus() kullanılarak düğme tıklandığında imleci giriş ref'ine odakladık. Bu mümkündür çünkü focus() girdi öğelerinin bir özniteliğidir ve bu nedenle başvuru, yöntemleri değerlendirebilecektir.

Bir üst bileşende oluşturulan referanslar, React.forwardRef() kullanılarak iletilerek alt bileşende değerlendirilebilir. Bir göz atalım.

İlk önce başka bir NewInput.js bileşeni oluşturalım ve buna aşağıdaki kodları ekleyelim.

 import { useRef, forwardRef } from "react"; const NewInput = forwardRef((props, ref) => { return <input placeholder={props.val} ref={ref} />; }); export default NewInput;

Bu bileşen props ve ref kabul eder. Ref'i ref prop'una ve props.val yer tutucu prop'una geçirdik. Normal React bileşenleri bir ref özniteliği almaz. Bu öznitelik yalnızca yukarıda gösterildiği gibi React.forwardRef ile sardığımızda kullanılabilir.

Daha sonra bunu ana bileşende kolayca çağırabiliriz.

 ... <NewInput val="Just an example" ref={inputRef} /> ...

Değişken Halleri Saklamak

Referanslar yalnızca DOM öğelerini işlemek için kullanılmaz, tüm bileşeni yeniden oluşturmadan değiştirilebilir değerleri depolamak için de kullanılabilirler.

Aşağıdaki örnek, bileşeni yeniden oluşturmadan bir düğmenin kaç kez tıklandığını tespit edecektir.

 import { useRef } from "react"; export default function App() { const countRef = useRef(0); const increment = () => { countRef.current++; console.log(countRef); }; return ( <div className="App"> <button onClick={increment}>Increment </button> </div> ); }

Yukarıdaki kodda buton tıklandığında countRef değerini arttırdık ve ardından konsola kaydettik. Değer konsolda gösterildiği gibi artırılsa da, doğrudan bileşenimizde değerlendirmeye çalışırsak herhangi bir değişiklik göremeyiz. Yalnızca yeniden oluşturulduğunda bileşende güncellenir.

useState eşzamansızken, useRef eşzamanlı olduğunu unutmayın. Başka bir deyişle, değer güncellendikten hemen sonra kullanılabilir.

useLayoutEffect Kancası

useEffect kancası gibi, useLayoutEffect , bileşen monte edildikten ve oluşturulduktan sonra çağrılır. Bu kanca, DOM mutasyonundan sonra tetiklenir ve bunu eşzamanlı olarak yapar. UseLayoutEffect, DOM mutasyonundan sonra eşzamanlı olarak çağrılmaktan ayrı olarak, useLayoutEffect ile aynı şeyi useEffect .

useLayoutEffect yalnızca DOM mutasyonu veya DOM ile ilgili ölçüm gerçekleştirmek için kullanılmalıdır, aksi takdirde useEffect kancasını kullanmalısınız. DOM mutasyon işlevleri için useEffect kancasının kullanılması, titreme gibi bazı performans sorunlarına neden olabilir, ancak useLayoutEffect , mutasyonlar gerçekleştikten sonra çalıştığı için bunları mükemmel şekilde işler.

Bu kavramı göstermek için bazı örneklere bakalım.

  1. Yeniden boyutlandırmada pencerenin genişliğini ve yüksekliğini alacağız.
 import {useState, useLayoutEffect} from 'react' const ResizeExample = () =>{ const [windowSize, setWindowSize] = useState({width: 0, height: 0}) useLayoutEffect(() => { const resizeWindow = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }) window.addEventListener('resize', resizeWindow) return () => window.removeEventListener('resize', resizeWindow) }, []) return ( <div> <p>width: {windowSize.width}</p> <p>height: {windowSize.height}</p> </div> ) } export default ResizeExample

Yukarıdaki kodda, width ve height özelliklerine sahip bir windowSize durumu oluşturduk. Ardından, pencere yeniden boyutlandırıldığında durumu sırasıyla mevcut pencerenin genişliğine ve yüksekliğine ayarlarız. Ayrıca, ayrıldığında kodu da temizledik. Temizleme işlemi, DOM manipülasyonunu temizlemek ve verimliliği artırmak için useLayoutEffect çok önemlidir.

  1. useLayoutEffect ile bir metni bulanıklaştıralım.
 import { useRef, useState, useLayoutEffect } from "react"; export default function App() { const paragraphRef = useRef(""); useLayoutEffect(() => { const { current } = paragraphRef; const blurredEffect = () => { current.style.color = "transparent"; current.style.textShadow = "0 0 5px rgba(0,0,0,0.5)"; }; current.addEventListener("click", blurredEffect); return () => current.removeEventListener("click", blurredEffect); }, []); return ( <div className="App"> <p ref={paragraphRef}>This is the text to blur</p> </div> ); }

Yukarıdaki kodda useRef ve useLayoutEffect birlikte kullandık. İlk önce paragrafımıza işaret etmek için bir ref, paragraphRef oluşturduk. Ardından, paragrafın ne zaman tıklandığını izlemek için bir tıklama olay dinleyicisi oluşturduk ve ardından tanımladığımız stil özelliklerini kullanarak paragrafı bulanıklaştırdık. Son olarak, removeEventListener kullanarak olay dinleyicisini temizledik.

useDispatch And useSelector Hooks

useDispatch , bir uygulamadaki eylemleri göndermek (tetiklemek) için bir Redux kancasıdır. Argüman olarak bir eylem nesnesi alır ve eylemi çağırır. useDispatch , kancanın mapDispatchToProps ile eşdeğeridir.

Öte yandan useSelector , Redux durumlarını değerlendirmek için bir Redux kancasıdır. Mağazadan tam Redux redüktörünü seçmek için bir fonksiyon alır ve ardından ilgili durumları döndürür.

Redux mağazamız Redux sağlayıcısı aracılığıyla bir React uygulamasına bağlandığında, useDispatch ile eylemleri çağırabilir ve useDispatch ile durumlara useSelector . Her Redux eylemi ve durumu bu iki kanca ile değerlendirilebilir.

Bu durumların React Redux (bir React uygulamasında Redux mağazasını değerlendirmeyi kolaylaştıran bir paket) ile birlikte geldiğini unutmayın. Çekirdek Redux kitaplığında mevcut değiller.

Bu kancaların kullanımı çok basittir. İlk olarak, sevk fonksiyonunu bildirmeli ve sonra onu tetiklemeliyiz.

 import {useDispatch, useSelector} from 'react-redux' import {useEffect} from 'react' const myaction from '...' const ReduxHooksExample = () =>{ const dispatch = useDispatch() useEffect(() => { dispatch(myaction()); //alternatively, we can do this dispatch({type: 'MY_ACTION_TYPE'}) }, []) const mystate = useSelector(state => state.myReducerstate) return( ... ) } export default ReduxHooksExample

Yukarıdaki kodda, useDispatch ve useSelector react-redux öğesinden içe aktardık. Ardından, bir useEffect kancasında eylemi gönderdik. Eylemi başka bir dosyada tanımlayabilir ve ardından burada çağırabilir veya doğrudan useEffect çağrısında gösterildiği gibi tanımlayabiliriz.

Eylemleri gönderdikten sonra, durumlarımız mevcut olacaktır. Daha sonra, gösterildiği gibi useSelector kancasını kullanarak durumu alabiliriz. Durumlar, useState kancasındaki durumları kullandığımız gibi kullanılabilir.

Bu iki kancayı göstermek için bir örneğe bakalım.

Bu konsepti göstermek için bir Redux mağazası, redüktör ve eylemler oluşturmamız gerekiyor. Burada işleri basitleştirmek için, JSONPlaceholder'ın sahte veritabanımızla Redux Toolkit kitaplığını kullanacağız.

Başlamak için aşağıdaki paketleri kurmamız gerekiyor. Aşağıdaki bash komutlarını çalıştırın.

 npm i redux @reduxjs/toolkit react-redux axios

İlk olarak, çalışanlarımızın API'si için azaltıcıyı ve eylemi işlemek için employeesSlice.js oluşturalım.

 import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import axios from "axios"; const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => { const { data } = await axios.get(endPoint); return data; }); const employeesSlice = createSlice({ name: "employees", initialState: { employees: [], loading: false, error: "" }, reducers: {}, extraReducers: { [fetchEmployees.pending]: (state, action) => { state.status = "loading"; }, [fetchEmployees.fulfilled]: (state, action) => { state.status = "success"; state.employees = action.payload; }, [fetchEmployees.rejected]: (state, action) => { state.status = "error"; state.error = action.error.message; } } }); export default employeesSlice.reducer;

Bu, Redux araç takımı için standart kurulumdur. Zaman uyumsuz eylemler gerçekleştirmek üzere Thunk ara yazılımına erişmek için createAsyncThunk kullandık. Bu, API'den çalışanların listesini getirmemize izin verdi. Daha sonra employeesSlice oluşturduk ve eylem türlerine bağlı olarak “yükleniyor”, “hata” ve çalışanların verilerini döndürdük.

Redux araç seti ayrıca mağazanın kurulumunu da kolaylaştırır. İşte mağaza.

 import { configureStore } from "@reduxjs/toolkit"; import { combineReducers } from "redux"; import employeesReducer from "./employeesSlice"; const reducer = combineReducers({ employees: employeesReducer }); export default configureStore({ reducer });;

Burada, redüktörleri bir araya getirmek için combineReducers ve mağazayı kurmak için Redux araç takımı tarafından sağlanan configureStore işlevini kullandık.

Bunu uygulamamızda kullanmaya devam edelim.

Öncelikle Redux'u React uygulamamıza bağlamamız gerekiyor. İdeal olarak, bu bizim uygulamamızın kökünde yapılmalıdır. Bunu index.js dosyasında yapmayı seviyorum.

 import React, { StrictMode } from "react"; import ReactDOM from "react-dom"; import store from "./redux/store"; import { Provider } from "react-redux"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <StrictMode> <App /> </StrictMode> </Provider>, rootElement );

Burada, yukarıda oluşturduğum mağazayı ve ayrıca Provider react-redux içe aktardım.

Ardından, tüm uygulamayı Provider işleviyle sardım, mağazayı ona aktardım. Bu, mağazayı uygulamamız boyunca erişilebilir kılar.

Daha sonra verileri almak için useDispatch ve useSelector kancalarını kullanmaya devam edebiliriz.

Bunu App.js dosyamızda yapalım.

 import { useDispatch, useSelector } from "react-redux"; import { fetchEmployees } from "./redux/employeesSlice"; import { useEffect } from "react"; export default function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(fetchEmployees()); }, [dispatch]); const employeesState = useSelector((state) => state.employees); const { employees, loading, error } = employeesState; return ( <div className="App"> {loading ? ( "Loading..." ) : error ? ( <div>{error}</div> ) : ( <> <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <h3>{`${employee.firstName} ${employee.lastName}`}</h3> </div> ))} </> )} </div> ); }

Yukarıdaki kodda, employeesSlice.js dosyasında oluşturulan fetchEmployees eylemini çağırmak için useDispatch kancasını kullandık. Bu, çalışanların durumunu uygulamamızda kullanılabilir hale getirir. Sonra durumları almak için useSelector kancasını kullandık. Daha sonra sonuçları employees üzerinden haritalandırarak görüntüledik.

Kullanım useHistory Kancası

React uygulamasında navigasyon çok önemlidir. Bunu birkaç yolla başarabilirken, React Router, bir React uygulamasında dinamik yönlendirme elde etmek için basit, verimli ve popüler bir yol sağlar. Ayrıca, React Router, yönlendiricinin durumunu değerlendirmek ve tarayıcıda gezinmeyi gerçekleştirmek için birkaç kanca sağlar, ancak bunları kullanmak için önce uygulamanızı düzgün bir şekilde kurmanız gerekir.

Herhangi bir React Router kancasını kullanmak için öncelikle uygulamamızı BrowserRouter ile BrowserRouter . Daha sonra rotaları Switch ve Route ile iç içe geçirebiliriz.

Ama önce aşağıdaki komutları çalıştırarak paketi kurmamız gerekiyor.

 npm install react-router-dom

Ardından aşağıdaki gibi uygulamamızı kurmamız gerekiyor. Bunu App.js yapmayı seviyorum.

 import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import Employees from "./components/Employees"; export default function App() { return ( <div className="App"> <Router> <Switch> <Route path='/'> <Employees /> </Route> ... </Switch> </Router> </div> ); }

Oluşturmak istediğimiz bileşenlerin sayısına bağlı olarak mümkün olduğunca çok Rotamız olabilir. Burada sadece Employees bileşenini oluşturduk. path özelliği, React Router DOM'a bileşenin yolunu söyler ve sorgu dizesi veya diğer çeşitli yöntemlerle değerlendirilebilir.

Burada sıra önemli. Kök rota, alt rotanın altına yerleştirilmelidir. Bu sırayı geçersiz kılmak için, kök yoluna exact anahtar kelimeyi eklemeniz gerekir.

 <Route path='/' exact > <Employees /> </Route>

Yönlendiriciyi kurduğumuza göre, artık uygulamamızda useHistory kancasını ve diğer React Yönlendirici kancalarını kullanabiliriz.

useHistory kancasını kullanmak için önce onu aşağıdaki gibi bildirmemiz gerekiyor.

 import {useHistory} from 'history' import {useHistory} from 'react-router-dom' const Employees = () =>{ const history = useHistory() ... }

Konsola geçmişi kaydedersek, onunla ilişkili birkaç özellik görürüz. Bunlar arasında block , createHref , go , goBack , goForward , length , listen , location , push , replace yer alır. Tüm bu özellikler faydalı olsa da, büyük olasılıkla history.push ve history.replace diğer özelliklerden daha sık kullanacaksınız.

Bir sayfadan diğerine geçmek için bu özelliği kullanalım.

Adlarına tıkladığımızda belirli bir çalışan hakkında veri almak istediğimizi varsayalım. Çalışan bilgilerinin görüntüleneceği yeni sayfaya gitmek için useHistory kancasını kullanabiliriz.

 function moveToPage = (id) =>{ history.push(`/employees/${id}`) }

Bunu aşağıdakileri ekleyerek Employee.js dosyamıza uygulayabiliriz.

 import { useEffect } from "react"; import { Link, useHistory, useLocation } from "react-router-dom"; export default function Employees() { const history = useHistory(); function pushToPage = (id) => { history.push(`/employees/${id}`) } ... return ( <div> ... <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <span>{`${employee.firstName} ${employee.lastName} `}</span> <button onClick={pushToPage(employee.id)}> » </button> </div> ))} </div> ); }

pushToPage işlevinde, çalışanın sayfasına gitmek ve yanına çalışan kimliğini iletmek için useHistory kancasındaki history kullandık.

useLocation Kancası

Bu kanca ayrıca React Router DOM ile birlikte gelir. Sorgu dizesi parametresiyle çalışmak için kullanılan çok popüler bir kancadır. Bu kanca, tarayıcıdaki window.location benzer.

 import {useLocation} from 'react' const LocationExample = () =>{ const location = useLocation() return ( ... ) } export default LocationExample

useLocation kancası, pathname , search parametresini, hash ve state . En sık kullanılan parametreler pathname ve search içerir, ancak aynı şekilde hash kullanabilir ve uygulamanızda çok şey state .

Location pathname özelliği, Route kurulumumuzda belirlediğimiz yolu döndürecektir. search , varsa sorgu arama parametresini döndürür. Örneğin, sorgumuza 'http://mywebsite.com/employee/?id=1' iletirsek, pathname /employee olur ve search ?id=1 olur.

Daha sonra çeşitli arama parametrelerini sorgu dizesi gibi paketleri kullanarak veya bunları kodlayarak alabiliriz.

useParams Kancası

Rotamızı yol özelliğinde bir URL parametresi ile kurarsak, bu parametreleri useParams kancasıyla anahtar/değer çiftleri olarak değerlendirebiliriz.

Örneğin, aşağıdaki Rotaya sahip olduğumuzu varsayalım.

 <Route path='/employees/:id' > <Employees /> </Route>

Rota, :id yerine dinamik bir kimlik bekleyecektir.

useParams kancası ile varsa kullanıcının geçtiği id'yi değerlendirebiliriz.

Örneğin, kullanıcının history.push ile fonksiyonda aşağıdakileri geçtiğini varsayarsak,

 function goToPage = () => { history.push(`/employee/3`) }

Bu URL parametresine erişmek için useParams kancasını aşağıdaki gibi kullanabiliriz.

 import {useParams} from 'react-router-dom' const ParamsExample = () =>{ const params = useParams() console.log(params) return( <div> ... </div> ) } export default ParamsExample

params konsola kaydedersek, şu nesneyi {id: "3"} alırız.

useRouteMatch Kancası

Bu kanca, eşleşme nesnesine erişim sağlar. Herhangi bir argüman sağlanmazsa, bir bileşene en yakın eşleşmeyi döndürür.

Match nesnesi, path (Rota'da belirtilen yolla aynı), URL , params nesnesi ve isExact dahil olmak üzere birkaç parametre döndürür.

Örneğin, rotaya dayalı bileşenleri döndürmek için useRouteMatch kullanabiliriz.

 import { useRouteMatch } from "react-router-dom"; import Employees from "..."; import Admin from "..." const CustomRoute = () => { const match = useRouteMatch("/employees/:id"); return match ? ( <Employee /> ) : ( <Admin /> ); }; export default CustomRoute;

Yukarıdaki kodda, useRouteMatch ile bir rotanın yolunu belirledik ve ardından kullanıcı tarafından seçilen rotaya bağlı olarak <Employee /> veya <Admin /> bileşenini oluşturduk.

Bunun çalışması için rotayı App.js dosyamıza eklememiz gerekiyor.

 ... <Route> <CustomRoute /> </Route> ...

Özel Bir Kanca Oluşturma

React belgelerine göre, özel bir kanca oluşturmak, bir mantığı yeniden kullanılabilir bir işleve çıkarmamızı sağlar. Ancak, React kancaları için geçerli olan tüm kuralların özel kancanız için de geçerli olduğundan emin olmanız gerekir. Bu öğreticinin üst kısmındaki React kancası kurallarını kontrol edin ve özel kancanızın her birine uygun olduğundan emin olun.

Özel kancalar, işlevleri bir kez yazmamıza ve gerektiğinde bunları yeniden kullanmamıza ve dolayısıyla DRY ilkesine uymamıza izin verir.

Örneğin, sayfamızda kaydırma pozisyonunu almak için aşağıdaki gibi özel bir kanca oluşturabiliriz.

 import { useLayoutEffect, useState } from "react"; export const useScrollPos = () => { const [scrollPos, setScrollPos] = useState({ x: 0, y: 0 }); useLayoutEffect(() => { const getScrollPos = () => setScrollPos({ x: window.pageXOffset, y: window.pageYOffset }); window.addEventListener("scroll", getScrollPos); return () => window.removeEventListener("scroll", getScrollPos); }, []); return scrollPos; };

Burada, bir sayfadaki kaydırma konumunu belirlemek için özel bir kanca tanımladık. Bunu başarmak için önce kaydırma konumunu depolamak için scrollPos adlı bir durum oluşturduk. Bu DOM'yi değiştireceğinden, useLayoutEffect yerine useEffect kullanmamız gerekiyor. x ve y kaydırma konumlarını yakalamak için bir kaydırma olay dinleyicisi ekledik ve ardından olay dinleyicisini temizledik. Sonunda kaydırma pozisyonuna döndük.

Bu özel kancayı uygulamamızın herhangi bir yerinde çağırarak ve diğer durumları kullanacağımız gibi kullanarak kullanabiliriz.

 import {useScrollPos} from './Scroll' const App = () =>{ const scrollPos = useScrollPos() console.log(scrollPos.x, scrollPos.y) return ( ... ) } export default App

Burada, yukarıda oluşturduğumuz özel kanca useScrollPos içe aktardık. Sonra onu başlattık ve ardından değeri konsolumuza kaydettik. Sayfayı kaydırırsak, kanca bize kaydırmanın her adımında kaydırma konumunu gösterecektir.

Uygulamamızda hayal edebileceğimiz hemen hemen her şeyi yapmak için özel kancalar oluşturabiliriz. Gördüğünüz gibi, bazı işlevleri gerçekleştirmek için yalnızca yerleşik React kancasını kullanmamız gerekiyor. Özel kancalar oluşturmak için üçüncü taraf kitaplıkları da kullanabiliriz, ancak bunu yaparsak, kancayı kullanabilmek için bu kitaplığı kurmamız gerekecek.

Çözüm

Bu eğitimde, uygulamalarınızın çoğunda kullanacağınız bazı kullanışlı React kancalarına bir göz attık. Ne sunduklarını ve uygulamanızda nasıl kullanacağınızı inceledik. Bu kancaları anlamanıza ve uygulamanıza uygulamanıza yardımcı olmak için birkaç kod örneğine de baktık.

Onları daha iyi anlamak için bu kancaları kendi uygulamanızda denemenizi tavsiye ederim.

React Dokümanlarından Kaynaklar

  • Kancalar SSS
  • Redux Araç Seti
  • Durum Kancasını Kullanma
  • Efekt Kancasını Kullanma
  • Kancalar API Referansı
  • Tepki Redux Kancaları
  • React Yönlendirici Kancaları