React'e Giriş, 4. Kısım

Yayınlanan: 2020-08-06

Sonunda, 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ı:

React ve Redux ile çoklu sayaçlar
React bileşenleri ve Redux tabanlı bir WordPress mağazası ile uygulanan çoklu sayaçlar.

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:

React ve Redux ile çoklu sayaçlar
Bugün oluşturmak istediğimiz uygulama.

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şenleri import gerektiğidir.
  • Şu ana kadar kullandığımız tüm içe aktarmalar olsa da, import ifademizde 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.
  • Counter bileşeni, beklediği herhangi bir sahne donanımı içermez. Çünkü onları mağazayı kullanarak dolduracağız (ve yeni prop counterId bu konuda bize yardımcı olacak).
  • Counter gibi CounterList de 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:

  • CounterList genişletilmiş versiyonunda yaptığımız ilk şey, ihtiyacımız olan yeni bağımlılıkları import . Özellikle, withSelect ve withDispatch ile 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 . withSelect daha ü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).
    • withSelect kullanmak 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 select argümanı vardır.
    • Bu anonim işlevde yaptığımız ilk şey, react-example/counters mağazamızdan getCounterIds seçicisini almaktır.
    • Bu anonim işlevin sonucu, CounterList bileşenimize eklemek istediğimiz aksesuarlardır. Bu durumda, değerleri getCounterIds counterIds listesidir.
    • withSelect sonucunu (unutmayın, bu sonuç daha yüksek dereceli bir bileşen veya yapacaksanız yeni bir işlevdir) withCounterIds adlı 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, MyComponent artık react-example/counters deposundan 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. withSelect ile tamamen aynıdır, ancak bize mağaza seçicilerine erişim vermek yerine, eylemlerine erişmemizi sağlar.
    • withDispatch , withSelect gibi, parametre olarak bir işlev bekler.
    • İlk parametresi şimdi dispatch olarak adlandırılıyor. Mağaza işlemlerine erişmek için kullanıyoruz.
    • dispatch kullanarak, react-example/counters mağazamızdan addCounter eylemini alırız.
    • Bileşenimizin beklediği addCounter işlevi, bağımsız değişkeni olmayan bir işlevdir. Ancak addCounter eyleminin 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, CounterList bileş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, prop addCounter her çağırdığımızda benzersiz bir kimlik oluşturmak için uuid paketini kullanırız.
    • withCounterAdder , bu withDispatch sonucudur ve withCounterIds ile benzerdir. Bu yüksek dereceli bileşeni mevcut bir bileşene uygulamak, var olan bileşene tam olarak istediğimiz şey olan bir addCounter sağlayacaktır.
  • Son olarak, CounterList bileşenimizi önce withCounterIds daha yüksek dereceli bileşeni uygulayarak (böylece CounterList bir counterIds listesi alır) ve ardından withCounterAdder addCounter alı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ı import aktarıyoruz.
  • Bileşenin kendisi olan Counter da değişmez. Hâlâ dört sahne bekliyor: value , onDelete , onIncrease ve onDecrease artık mağazadan alacağını biliyoruz.
  • İlk olarak, mağazamızdan sayacın value almak için withSelect kullanın.
    • Mağaza birçok sayacın değerini koruyor, bu yüzden tam olarak hangi sayacı istediğimizi söylemeliyiz. Neyse ki, CounterList her bir sayaç kimliğini bir bileşenle eşleştirdiğinde, her bir Counter bir counterId , 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, CounterList ile gördüğümüz örneğe çok benzer. getCounterValue seçicisini alıyoruz ve counterId prop kullanarak doğru value döndürüyoruz. Anonim işlevin şimdi ikinci bir props olduğuna dikkat edin: bileşenin destek listesi.
  • Ardından, Counter ihtiyaç duyduğu eylemleri doldurmak için withDispatch kullanırız.
    • Mağazamızda setCounterValue ve removeCounter eylemlerine sahip olduğumuzu biliyoruz.
    • Her iki eylem de, az önce gördüğümüz gibi withDispatch iç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.
    • setCounterValue ayrıca ayarlamak istediğimiz yeni değeri de bilmelidir. Counter bileş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, withSelect kullanarak sayacın mevcut değerini aldık, bu da value adında bir prop olduğu anlamına geliyor.
    • withDispatch uygulamasının sonucu, bileşenimizin beklediği üç işleve sahip olacak daha yüksek dereceli bir bileşendir: onIncrease , mevcut counterId değerine 1 ekleyen anonim bir işlevdir; onDecrease , aynı şeyi yapan, ancak 1 çıkaran başka bir anonim işlevdir; ve onDelete , mağazadan removeCounter eylemini kullanan bir işlevdir.
  • Son olarak, az önce oluşturduğumuz üst düzey bileşenleri ( withValue ve withActions ) Counter bileşenimize uygularız. Bu sefer @wordpress/compose compose sağlanan yardımcı oluşturma işlevini kullandım, ancak bunun daha önce CounterList ile 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.