React'e Giriş, 4. Kısım
Yayınlanan: 2020-08-06Sonunda, bu küçük React tanıtımının son kısmına geldik. Mantıksal olarak, birçok şeyi sıraya koydum, bu nedenle gelecekte JS geliştirme yığınının farklı yönlerine odaklanacağımız daha fazla gönderi yazmayı dışlamıyorum, ancak bu son gönderiyle zaten sahip olacağınızı düşünüyorum. Kendi başınıza büyümek ve daha iyi olmak için gerekli temel kavramlar.
Geçen haftayı çok ilginç bir noktada noktaladık. Amacımız, istediğiniz zaman ekleyebileceğiniz veya kaldırabileceğiniz birden fazla sayaç içeren küçük bir uygulama oluşturmaktı:

Son gönderiyi bitirdiğimizde, uygulamamızın durumunu takip etmek için harika bir mağazamız vardı. Bu, sonunda kullanıcı arayüzünü oluşturmak için ihtiyacımız olan tüm temel bilgilere zaten sahip olduğumuz anlamına gelir.
Bugün, arayüzün mağazadaki durumu göstermesi ve kullanıcı etkileşimlerinin söz konusu durumu güncellemesi için React bileşenlerini Redux tabanlı WordPress mağazamızla nasıl bağlayacağımızı göreceğiz.
Geçen Haftanın Ödevinin Çözümü
Ama bunu yapmadan önce, geçen hafta sana bıraktığım ödevi hızlıca gözden geçirelim. Esasen, mağazayı artık bir sözlük değil, bir dizi nesne kullanacak şekilde yeniden uygulamanızı istedim:
const originalStore = { x: 1, y: 2, }; const newStore = [ { id: 'x', value: 1 }, { id: 'y', value: 2 }, ];Bu güncellemeyi mağazanıza uygulamak için hangi değişiklikleri yaptınız? Peki, hepsini hızlıca gözden geçirelim!
Hareketler
Mağaza eylemleri, zaten sahip olduklarımızla tamamen aynı, bu yüzden gerekli olmadığı için burada hiçbir şeyi değiştirmemişsinizdir. Geçen hafta gördüğünüz gibi, eylemler mağazayı doğrudan güncellemez (veya erişmez) ve bu nedenle mağazamızın temel yapısındaki herhangi bir değişikliğin burada herhangi bir etkisi olmaz.
Action, bir "güncelleme isteği" sinyalini veren bir nesne oluşturan basit işlevlerdir ve bu tam olarak actions.js dosyamızda hala bulunan şeydir:
export function addCounter( counterId ) { return { type: 'ADD_COUNTER', counterId, }; } export function removeCounter( counterId ) { return { type: 'REMOVE_COUNTER', counterId, }; } export function setCounterValue( counterId, value ) { return { type: 'SET_COUNTER_VALUE', counterId, value, }; }redüktör
Düşürücü, önceki duruma ve gönderilen bir eyleme göre durumu güncellemekten sorumlu işlevdir. Bu durumda, uygulamamızın durumunu saklayan veri yapısını değiştirmek istediğimizden, redüktörü yeniden uygulamamız gerekiyor:
import { map, find, without } from 'lodash'; export default function reducer( state = [], action ) { switch ( action.type ) { case 'ADD_COUNTER': return [ ...state, { id: action.counterId, value: 0 }, ]; case 'REMOVE_COUNTER': return without( state, find( state, { id: action.counterId } ) ); case 'SET_COUNTER_VALUE': return map( state, ( counter ) => action.id === action.counterId ? { ...counter, value: action.value } : counter ); } return state; } Tahmin edebileceğiniz gibi, burada tek yapmamız gereken, durumun varsayılan değerini boş nesneden {} boş bir diziye [] değiştirmekti. Ardından, eylemlerin yeni mağazayı doğru şekilde güncellemesi için switch bloğumuzdaki her bir case ayarlamamız yeterlidir. Esasen bu, dizideki nesneleri eklemek, çıkarmak veya güncellemektir.
seçiciler
Son olarak, selectors.js dosyamız var. Geçen hafta gördüğümüz gibi, bir seçici, uygulamamızın mevcut durumunu ve ihtiyaç duyabileceğimiz diğer parametreleri alır ve bu durumdan istenen değeri alır. Durumun nasıl saklandığını değiştirdiğimiz için seçicilerimizin gövdesini de değiştirmemiz gerekiyor:
import { map, find } from 'lodash'; export function getCounterIds( state ) { return map( state, 'id' ); } export function getCounterValue( state, counterId ) { const counter = find( state, { id: counterId } ) || {}; return counter.value; }Yine, önemli değil… ama umarım bunu doğru anlamışsınızdır
Örneğimizde yalnızca iki seçicimiz vardı, bu nedenle yalnızca iki işlevi yeniden uygulamamız gerekiyor. İlki, map ile çok kolay bir şekilde elde edebileceğimiz tüm sayaçlarımızın tanımlayıcılarını döndürmekle sorumludur. İkincisi belirtilen sayacın değerini döndürür (veya verilen sayaç kimliği mağazamızda bulunamazsa undefined ), bu nedenle tek yapmanız gereken counterId yardımcı işlevini kullanarak id counterId olan sayacı aramak ve value find ve döndürmek ilgili sayacın özelliği (varsa).
Son Bir Not
Sizden mağazanızda ince ayar yapmanızı istememin nedeni, şu dersi öğrenebilmenizdi: Bunun gibi mağazaları kullanmanın ana avantajı, kara kutular gibi davranmalarıdır - dahili olarak nasıl düzenlendiğini tamamen değiştirebilirsiniz ve her şey beklendiği gibi çalışacaktır. , arayüzü (yani eylemler ve seçiciler) değişmediği sürece.
Kullanıcı Arayüzümüzün Bileşenlerini Yeniden Yazmak
Kullanıcılarımızın istedikleri zaman sayaç ekleyip çıkarabilmelerini istiyoruz:

ve her sayacı birbirinden bağımsız olarak yönetin. 2. bölümde uyguladığımız kullanıcı arayüzünde sahip olduğumuz değişikliklere hangi değişiklikleri uygulamamız gerekiyor? Açıkçası (1) her sayacı bir Sil düğmesi içerecek şekilde değiştirmemiz ve (2) yeni sayaçlar eklemek için yeni bir düğme olacak şekilde uygulamayı değiştirmemiz gerekiyor. Öyleyse yapalım şunu!
İlk olarak, src/components/counter.js dosyasını açın ve yeni Sil düğmesini ve yeni bir prop onDelete ekleyin, böylece kullanıcı düğmeyi tıkladığında sayaç silinir:
const Counter = ( { value, onIncrease, onDecrease, onDelete } ) => ( <div> <div>Counter: <strong>{ value }</strong></div> <button onClick={ onIncrease }>+</button> <button onClick={ onDecrease }>-</button> <button onClick={ onDelete }>Delete</button> </div> ); export default Counter; Ardından, (a) kullanıcının eklediği tüm sayaçları ve (b) yenilerini ekleme düğmesini oluşturmaktan sorumlu olacak yeni bir bileşen oluşturun. Aşağıdaki gibi bir src/components/counter-list.js dosyası oluşturmanızı öneririm:
import Counter from './counter'; const CounterList = ( { addCounter, counterIds } ) => ( <div> { counterIds.map( ( id ) => ( <Counter key={ id } counterId={ id } /> ) ) } <button onClick={ addCounter }>Add Counter</button> </div> ); export default CounterList;Bu yeni bileşenin bahsetmeye değer birkaç ilginç özelliği var:
- Başka bir bileşen (
Counter) kullanan bir bileşendir (CounterList). Öğreticimizde bunun herhangi bir örneğini henüz görmedik, bu yüzden… şimdi bir tane var! Hatırlamanız gereken tek şey, kullanmak istediğiniz tüm bileşenleriimportgerektiğidir. - Şu ana kadar kullandığımız tüm içe aktarmalar olsa da,
importifademizde küme parantezleri yok (import Counter vs import {Counter}). Bunun nedeni, Sayacın artık varsayılan bir dışa aktarma olması ve varsayılan dışa aktarmaların kaşlı ayraçlarla içe aktarılmamasıdır. - CounterList bileşeni iki özellik alır: addCounter, yeni sayaçlar eklememize izin veren eylemdir ve counterIds, uygulamamızın sahip olduğu tüm sayaçların tanımlayıcılarını içeren bir dizidir.
- Her bir sayacı oluşturmak için, ID dizisini Counter örnekleriyle eşleştiririz.
- Bu haritada, Counter bileşeninin iki yeni özelliği vardır: key ve counterId. key, React tarafından işleme motorunu optimize etmek için gereken bir destektir. counterId daha sonra tartışılacak bir şeydir.
-
Counterbileşeni, beklediği herhangi bir sahne donanımı içermez. Çünkü onları mağazayı kullanarak dolduracağız (ve yeni propcounterIdbu konuda bize yardımcı olacak). -
CountergibiCounterListde bir dışa aktarma varsayılanıdır.
Ana Dosya
Uygulamamız artık tek bir sayaç değil, bir sayaç listesi gösterdiğinden, index.js yeni oluşturduğumuz yeni bileşeni oluşturacak şekilde ayarlamamız gerekiyor: CounterList . Üstelik tekli sayacımızın durumunu yönetmek için 2. bölümde yazdığımız tüm kodlardan da kurtulabiliriz çünkü artık uygun bir WordPress mağazamız var.
Tüm bunları hesaba katarak, index.js şöyle görünmelidir:
// Import dependencies import { render } from '@wordpress/element'; import './store'; import CounterList from './components/counter-list'; // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); render( <CounterList />, wrapper ); Gördüğünüz gibi, sadece mağazamızı ve CounterList bileşenini içe aktarıyoruz ve ikincisini ( @wordpress/element paketinin render işlevini kullanarak) bir DOM düğümünde oluşturuyoruz.
Ne yazık ki, hiçbir şey beklendiği gibi çalışmıyor (henüz), ancak bunun nedeni, hiçbir bileşenimizi mağazaya bağlamamış olmamızdır.
React Bileşenlerini Redux Mağazasına Bağlama
Serin! İhtiyacımız olan her şey hazır ve hedefimize sadece bir adım uzaktayız. Bir yandan, uygulamamızın durumunu sorgulamak için seçicileri ve onu güncellemek için eylemleri olan bir mağazamız var. Öte yandan, bu durumu UI'da görselleştirmek için gerekli bileşenlere sahibiz. Eksik olan tek şey iki parçayı birbirine yapıştırmak…
CounterList Mağazaya Bağlama
CounterList , iki özellik gerektiren basit bir bileşendir. Bir yandan, uygulamamızda aktif olan tüm bileşenlerin tanımlayıcılarını içeren bir liste bekler: counterIds . Öte yandan, yeni sayaçlar eklemek için bir geri arama da bekler: addCounter .
Bu öğreticinin 3. bölümünde mağazamızda tanımladığımız seçicilere ve eylemlere bir göz atarsanız, böyle bir seçici ve eylemimiz olduğunu göreceksiniz. Mağaza gerçekten de "sahip olduğumuz tüm bileşenlerin tanımlayıcılarını içeren bir liste" döndüren getCounterIds seçicisine sahip. Ayrıca, uygulamamıza yeni bir sayaç ekleyen addCounter eylemine de sahiptir (buna benzersiz bir tanımlayıcı vermeniz şartıyla). Şimdi bunları bileşenimizde nasıl kullanabileceğimizi görelim.

Mağazayı kaydettirmek için zaten kullandığımız @wordpress/data paketi, bir mağazadan türetilen özelliklere sahip mevcut bir bileşeni genişletmek için birkaç yüksek dereceli bileşen sunar: withSelect ve withDispatch . Bu, bir bileşene withSelect ve/veya withDispatch uygulayarak, söz konusu bileşeni bir mağazada tanımlanan değerleri kullanarak istediğimiz props genişletebileceğimiz anlamına gelir.
Bunu bir örnekle görelim:
import { withSelect, withDispatch } from '@wordpress/data'; import { v4 as uuid } from 'uuid'; import Counter from './counter'; const CounterList = ( { addCounter, counterIds } ) => ( <div> { counterIds.map( ( id ) => ( <Counter key={ id } counterId={ id } /> ) ) } <button onClick={ addCounter }>Add Counter</button> </div> ); const withCounterIds = withSelect( ( select ) => { const { getCounterIds } = select( 'react-example/counters' ); return { counterIds: getCounterIds(), }; } ); const withCounterAdder = withDispatch( ( dispatch ) => { const { addCounter } = dispatch( 'react-example/counters' ); return { addCounter: () => addCounter( uuid() ), }; } ); export default withCounterAdder( withCounterIds( CounterList ) );Burada bir sürü şey olduğunu biliyorum, o yüzden adım adım gidelim:
-
CounterListgenişletilmiş versiyonunda yaptığımız ilk şey, ihtiyacımız olan yeni bağımlılıklarıimport. Özellikle,withSelectvewithDispatchile içe aktarırız. Ardından, garip bir içe aktarma işlemimiz var:uuid. Bu, benzersiz rastgele tanımlayıcılar oluşturan bir pakettir ve buna neden ihtiyacımız olduğunu birazdan anlayacaksınız. - Bileşenin kendisi (
CounterList) değişmedi. Yine de, kullanıcı arayüzünü oluşturmak için ihtiyaç duyduğu iki desteği alacağını varsayar: bir sayaç kimlikleri listesi ve yeni sayaçlar eklemek için bir eylem. - Ardından,
withSelect.withSelectdaha üst düzey bir bileşendir, ancak bunu "başka bir işlevi döndüren bir işlev" olarak düşünebilirsiniz. Ortaya çıkan işlev, mevcut bir bileşenin yeteneklerini artıracak bir şeydir (okumaya devam edin ve yakında her şey anlam kazanacaktır).-
withSelectkullanmak için önce onu bir fonksiyonla parametre olarak çağırmanız gerekir. Bizim durumumuzda isimsiz bir fonksiyon yarattık. - Anonim işlevin, mağazalarınızda kayıtlı tüm seçicilere erişmenizi sağlayan bir
selectargümanı vardır. - Bu anonim işlevde yaptığımız ilk şey,
react-example/countersmağazamızdangetCounterIdsseçicisini almaktır. - Bu anonim işlevin sonucu,
CounterListbileşenimize eklemek istediğimiz aksesuarlardır. Bu durumda, değerlerigetCounterIdscounterIdslistesidir. -
withSelectsonucunu (unutmayın, bu sonuç daha yüksek dereceli bir bileşen veya yapacaksanız yeni bir işlevdir)withCounterIdsadlı bir değişkene kaydederiz. Artık bu üst düzey bileşeni/fonksiyonu istediğimiz herhangi bir bileşene şu şekilde uygulayabiliriz:withCounterIds(MyComponent). Bunu yapmanın sonucu,MyComponentartıkreact-example/countersdeposundan alınan bir değerle doğru şekilde ayarlanmışcounterIdsözelliğine sahip olmasıdır.
-
- Ardından
withDispatchüst düzey bileşenini/işlevini kullanırız.withSelectile tamamen aynıdır, ancak bize mağaza seçicilerine erişim vermek yerine, eylemlerine erişmemizi sağlar.-
withDispatch,withSelectgibi, parametre olarak bir işlev bekler. - İlk parametresi şimdi
dispatcholarak adlandırılıyor. Mağaza işlemlerine erişmek için kullanıyoruz. -
dispatchkullanarak,react-example/countersmağazamızdanaddCountereylemini alırız. - Bileşenimizin beklediği
addCounterişlevi, bağımsız değişkeni olmayan bir işlevdir. AncakaddCountereyleminin bir argümana ihtiyacı var: yeni sayacın sahip olması gereken benzersiz tanımlayıcı. Bu uyuşmazlığı çözmek için tek yapmamız gereken,CounterListbileşenlerinin beklediği destekle eşleşen adsız bir işlev döndürmek (yani bağımsız değişkeni olmayan bir işlev) ve bu işlevin yürütülmesi mağazadaki fiili eylemi çağıracaktır. Mağazadaki eylem benzersiz bir tanımlayıcıya ihtiyaç duyduğundan, propaddCounterher çağırdığımızda benzersiz bir kimlik oluşturmak içinuuidpaketini kullanırız. -
withCounterAdder, buwithDispatchsonucudur vewithCounterIdsile benzerdir. Bu yüksek dereceli bileşeni mevcut bir bileşene uygulamak, var olan bileşene tam olarak istediğimiz şey olan biraddCountersağlayacaktır.
-
- Son olarak,
CounterListbileşenimizi öncewithCounterIdsdaha yüksek dereceli bileşeni uygulayarak (böyleceCounterListbircounterIdslistesi alır) ve ardındanwithCounterAdderaddCounteralır).
Ve bu kadar! Artık ilk bileşeninizi bir mağazaya başarıyla bağladınız; bu, artık kullanıcı arayüzünüze sayaçlar ekleyebilmeniz (ve bunları görebilmeniz) gerektiği anlamına gelir.
Her Counter Mağazaya Bağlanması
Şimdi Counter bileşenimizi mağazadan ihtiyaç duyduğu sahne malzemelerini alacak şekilde değiştirelim. Tahmin edebileceğiniz gibi, tek yapmamız gereken az önce yaptığımız işlemi tekrarlamak: withSelect ve withDispatch aracılığıyla react-example/counters birkaç seçici ve eylem kullanın ve elde edilen yüksek dereceli bileşenleri Counter öğesine uygulayın.
Son kaynak koduna bakarak başlayalım:
import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; const Counter = ( { value, onDelete, onIncrease, onDecrease } ) => ( <div> <div>Counter: <strong>{ value }</strong></div> <button onClick={ onIncrease }>+</button> <button onClick={ onDecrease }>-</button> <button onClick={ onDelete }>Delete</button> </div> ); const withValue = withSelect( ( select, { counterId } ) => { const { getCounterValue } = select( 'react-example/counters' ); return { value: getCounterValue( counterId ), }; } ); const withActions = withDispatch( ( dispatch, { counterId, value } ) => { const { setCounterValue, removeCounter, } = dispatch( 'react-example/counters' ); return { onIncrease: () => setCounterValue( counterId, value + 1 ), onDecrease: () => setCounterValue( counterId, value - 1 ), onDelete: () => removeCounter( counterId ), }; } ); export default compose( withValue, withActions )( Counter );- Her şeyden önce, ihtiyacımız olacak bağımlılıkları
importaktarıyoruz. - Bileşenin kendisi olan
Counterda değişmez. Hâlâ dört sahne bekliyor:value,onDelete,onIncreaseveonDecreaseartık mağazadan alacağını biliyoruz. - İlk olarak, mağazamızdan sayacın
valuealmak içinwithSelectkullanın.- Mağaza birçok sayacın değerini koruyor, bu yüzden tam olarak hangi sayacı istediğimizi söylemeliyiz. Neyse ki,
CounterListher bir sayaç kimliğini bir bileşenle eşleştirdiğinde, her birCounterbircounterId, hatırladınız mı? Peki, mağazamıza tam olarak ilgilendiğimiz değeri bu şekilde söyleyebiliriz… - Bu anonim işlevin gövdesi,
CounterListile gördüğümüz örneğe çok benzer.getCounterValueseçicisini alıyoruz vecounterIdprop kullanarak doğruvaluedöndürüyoruz. Anonim işlevin şimdi ikinci birpropsolduğuna dikkat edin: bileşenin destek listesi.
- Mağaza birçok sayacın değerini koruyor, bu yüzden tam olarak hangi sayacı istediğimizi söylemeliyiz. Neyse ki,
- Ardından,
Counterihtiyaç duyduğu eylemleri doldurmak içinwithDispatchkullanırız.- Mağazamızda
setCounterValueveremoveCountereylemlerine sahip olduğumuzu biliyoruz. - Her iki eylem de, az önce gördüğümüz gibi
withDispatchiçinde tanımladığımız anonim işlevin ikinci argümanında bulunan güncelleme veya silme için sayacın tanımlayıcısına ihtiyaç duyar. -
setCounterValueayrıca ayarlamak istediğimiz yeni değeri de bilmelidir.Counterbileşenimizin çalışması gerektiğinden (biri mevcut değerini artırmak, diğeri ise mevcut değerini azaltmak için), bir ekleme (veya çıkarma) yapabilmemiz için mevcut değerin ne olduğunu da bilmemiz gerekir. Neyse ki,withSelectkullanarak sayacın mevcut değerini aldık, bu davalueadında bir prop olduğu anlamına geliyor. -
withDispatchuygulamasının sonucu, bileşenimizin beklediği üç işleve sahip olacak daha yüksek dereceli bir bileşendir:onIncrease, mevcutcounterIddeğerine1ekleyen anonim bir işlevdir;onDecrease, aynı şeyi yapan, ancak1çıkaran başka bir anonim işlevdir; veonDelete, mağazadanremoveCountereylemini kullanan bir işlevdir.
- Mağazamızda
- Son olarak, az önce oluşturduğumuz üst düzey bileşenleri (
withValuevewithActions)Counterbileşenimize uygularız. Bu sefer@wordpress/composecomposesağlanan yardımcı oluşturma işlevini kullandım, ancak bunun daha önceCounterListile yaptığımızın tamamen aynısı olduğuna dikkat edin.
Ve bu kadar! Artık bir mağazaya tepki veren ve onu değiştiren birkaç bileşenle çalışan bir örneğiniz var!
Özetle
WordPress'teki React/Redux'a bu kısa giriş boyunca, iyi kullanıcı arayüzleri oluşturmak için gerekli tüm malzemeleri gördük. Özünde, bileşenlerin özellikleri alan ve HTML oluşturan saf işlevler olması gerektiğini, uygulamamızın durumunu UI'den bağımsız olarak korumak için mağazaları nasıl kullanabileceğimizi ve birbirimizi nasıl entegre edebileceğimizi gördük.
React bileşenlerini bir mağazadan değer alacak şekilde genişletmek, withSelect ve withDispatch kullanmak kadar kolaydır ve gerekli eksik parçaları ekleyen daha yüksek dereceli bir bileşen oluşturur.
Unsplash'ta Dmitry Nucky Thompson tarafından Öne Çıkan Resim.
