Redux Reducer'da Mağazanın İlk Durumunu Okuyun


85

Bir Redux uygulamasındaki başlangıç ​​durumu iki şekilde ayarlanabilir:

Mağazanıza ilk durumu iletirseniz, bu durumu mağazadan nasıl okursunuz ve onu redüktörlerinizde ilk argüman haline getirirsiniz?

Yanıtlar:


185

TL; DR

Olmadan combineReducers()veya benzer manuel kod, initialStateher zaman yener state = ...çünkü indirgeyicide stateredüktör geçirilen olduğunu initialState ve değil undefined ES6 argüman sözdizimi, bu durumda uygulanan almaz böylece.

İle combineReducers()davranış daha farklı. Durumu belirtilen redüktörler initialStatebunu alacak state. Diğer indirgeyiciler alacak undefined ve bu nedenle belirledikleri state = ...varsayılan argümana geri dönecekler .

Genel olarak, initialStateredüktör tarafından belirtilen devlet üzerinden kazanır. Bu, indirgeyicilerin varsayılan bağımsız değişkenler olarak kendileri için anlamlı olan ilk verileri belirlemelerine izin verir, ancak aynı zamanda bazı kalıcı depolama alanlarından veya sunucudan depoyu nemlendirirken mevcut verilerin (tamamen veya kısmen) yüklenmesine izin verir.

Öncelikle tek bir redüktörünüzün olduğu bir durumu ele alalım.
Kullanmadığını söyle combineReducers().

Düşürücünüz şöyle görünebilir:

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT': return state + 1;
  case 'DECREMENT': return state - 1;
  default: return state;
  }
}

Şimdi onunla bir mağaza oluşturduğunuzu varsayalım.

import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0

Başlangıç ​​durumu sıfırdır. Neden? Çünkü ikinci argüman createStoreoldu undefined. Bu, stateredüktörünüze ilk kez geçer. Redux başlatıldığında, durumu doldurmak için "kukla" bir eylem gönderir. Yani counterredüktörünüz stateeşit olarak çağrıldı undefined. Varsayılan argümanı "etkinleştiren" durum tam olarak budur. Bu nedenle, stateşimdi 0varsayılan statedeğere ( state = 0) göre. Bu durum ( 0) döndürülecek.

Farklı bir senaryo düşünelim:

import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42

Neden bu sefer 42değil 0mi? Çünkü ikinci argüman olarak createStoreçağrıldı 42. Bu argüman state, kukla eylemle birlikte indirgeyicinize aktarılır. Bu sefer statetanımsız değildir (bu 42!), Dolayısıyla ES6 varsayılan bağımsız değişken sözdiziminin bir etkisi yoktur. stateOlduğu 42ve 42redüktör döndürülür.


Şimdi, kullandığınız bir durumu ele alalım combineReducers().
İki azaltıcınız var:

function a(state = 'lol', action) {
  return state;
}

function b(state = 'wat', action) {
  return state;
}

Oluşturduğu redüktör combineReducers({ a, b })şuna benzer:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

Eğer createStoreolmadan initialStateararsak, stateto başlatılacaktır {}. Bu nedenle, state.ave state.bolacak undefinedo çağıran süreye göre ave bdüşürücüler. Hem ave bdüşürücüler alacak undefinedşekilde onların state argümanları ve bunlar varsayılan belirtirseniz statedeğerleri, bu iade edilir. Bu, birleşik indirgeyicinin { a: 'lol', b: 'wat' }ilk çağrıda bir durum nesnesi döndürmesinin şeklidir.

import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }

Farklı bir senaryo düşünelim:

import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }

Şimdi initialStateargüman olarak belirttim createStore(). Kombine düşürücü dönen devlet biçerdöver ben için belirtilen başlangıç durumu aile redüktör 'wat'varsayılan argüman belirtti bredüktör kendisini seçti.

Kombine redüktörün ne yaptığını hatırlayalım:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

Bu durumda, stategeri dönmemesi için belirtildi {}. Bu, aalanı eşit 'horse', ancak balanı olmayan bir nesneydi . Bu yüzden aredüktör alınan 'horse'olarak onun stateve memnuniyetle geri dönmesine rağmen bredüktör alınan undefinedolarak onun stateve böylece dönen onun fikrini temerrüt state(bizim örneğimizde 'wat'). { a: 'horse', b: 'wat' }Karşılığında böyle alıyoruz .


Özetlemek gerekirse, Redux kurallarına sadık kalırsanız undefinedve stateargüman olarak çağrıldıklarında indirgeyicilerden başlangıç ​​durumunu döndürürseniz (bunu uygulamanın en kolay yolu stateES6 varsayılan argüman değerini belirlemektir ), sahip olacaksınız kombine redüktörler için güzel ve kullanışlı bir davranış. İşleve ilettiğiniz initialStatenesnede karşılık gelen değeri tercih edeceklerdir createStore(), ancak herhangi birini geçmediyseniz veya ilgili alan ayarlanmamışsa, statebunun yerine indirgeyici tarafından belirtilen varsayılan bağımsız değişken seçilir.Bu yaklaşım, mevcut verilerin hem başlatılmasını hem de hidrasyonunu sağladığından iyi çalışır, ancak verileri korunmadıysa bireysel indirgeyicilerin durumlarını sıfırlamasına izin verir. Elbette bu modeli combineReducers()birçok düzeyde kullanabileceğiniz gibi yinelemeli olarak uygulayabilir veya hatta redüktörleri arayarak ve onlara durum ağacının ilgili bölümünü vererek azaltıcıları manuel olarak oluşturabilirsiniz.


3
Büyük detay için teşekkürler - tam olarak aradığım şey. API'nizin buradaki esnekliği harika. Görmediğim kısım, "çocuk" redüktörün, kullanılan anahtara bağlı olarak, başlangıç ​​durumunun hangi parçasına ait olduğunu anlayacağıdır combineReducers. Tekrar çok teşekkür ederim.
cantera

Bu cevap için teşekkürler, Dan. Cevabınızı doğru bir şekilde sindiriyorsam, bir azaltıcının eylem türü aracılığıyla bir başlangıç ​​durumu belirlemenin en iyisi olacağını mı söylüyorsunuz? Örneğin, GET_INITIAL_STATE ve bu eylem türüne bir gönderim çağırın, diyelim ki bir componentDidMount geri çağrısı mı?
Con Antonakos

@ConAntonakos Hayır, bunu kastetmiyorum. İndirgeyiciyi tam olarak tanımladığınız ilk durumu seçmeyi kastediyorum, örneğin function counter(state = 0, action)veya function visibleIds(state = [], action).
Dan Abramov

1
@DanAbramov: createtore () içindeki ikinci bağımsız değişkeni kullanarak, SPA sayfası yeniden yüklendiğinde durumu, başlangıç ​​değerleri yerine redux içindeki son depolanmış durumla nasıl yeniden sulandırabilirim. Sanırım tüm mağazayı nasıl önbelleğe alabileceğimi ve yeniden yüklerken onu nasıl koruyabileceğimi ve createtore işlevine aktarabileceğimi soruyorum.
jasan

1
@jasan: localStorage kullanın. egghead.io/lessons/…
Dan Abramov

5

Özetle: Redux, ilk durumu redüktörlere geçirendir, hiçbir şey yapmanıza gerek yoktur.

Aradığınızda createStore(reducer, [initialState])size Redux ilk eylem geldiğinde redüktör geçirilecek ilk devlet olduğunu bilmelerini sağlayabilirsiniz edilmektedir.

Bahsettiğiniz ikinci seçenek, yalnızca mağazayı oluştururken bir başlangıç ​​durumunu geçmemiş olmanız durumunda geçerlidir. yani

function todoApp(state = initialState, action)

durum yalnızca Redux tarafından geçilen bir durum yoksa başlatılacaktır


1
Yanıt verdiğiniz için teşekkürler - indirgeyici bileşimi durumunda, mağazaya ilk durumu uyguladıysanız, alt / alt indirgeyiciye ilk durum ağacının hangi kısmına sahip olduğunu nasıl söylersiniz? Örneğin, bir a'nız varsa menuState, menü düşürücüye state.menumağazadan değerini okumasını ve bunu başlangıç ​​durumu olarak kullanmasını nasıl söylerim ?
cantera

Teşekkürler, ama sorum açık olmamalı. Düzenlemeden önce başkalarının yanıt verip vermediğini görmek için bekleyeceğim.
cantera

1

Bu durumu mağazadan nasıl okursunuz ve onu redüktörünüzdeki ilk argüman haline getirirsiniz?

kombineReducers () işi sizin yerinize yapar. Bunu yazmanın ilk yolu pek yardımcı olmuyor:

const rootReducer = combineReducers({ todos, users })

Ama diğerinin eşdeğeri daha açık:

function rootReducer(state, action) {
   todos: todos(state.todos, action),
   users: users(state.users, action)
}

0

Umarım bu isteğinize cevap verir (bunu, intialState'i geçerken ve o duruma geri dönerken redüktörleri başlatmak olarak anladım)

Biz böyle yapıyoruz (uyarı: Typescript kodundan kopyalandı).

Bunun özü if(!state), mainReducer (fabrika) işlevindeki testtir.

function getInitialState(): MainState {

     return {
         prop1:                 'value1',
         prop1:                 'value2',
         ...        
     }
}



const reducer = combineReducers(
    {
        main:     mainReducer( getInitialState() ),
        ...
    }
)



const mainReducer = ( initialState: MainState ): Reducer => {

    return ( state: MainState, action: Action ): MainState => {

        if ( !state ) {
            return initialState
        }

        console.log( 'Main reducer action: ', action ) 

        switch ( action.type ) {
            ....
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.