Bir eylem oluşturucuda Redux durumuna erişiyor musunuz?


296

Aşağıdakilere sahip olduğumu söyle:

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
  }
}

Ve bu eylem oluşturucuda, küresel mağaza durumuna (tüm redüktörler) erişmek istiyorum. Bunu yapmak daha mı iyi?

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

veya bu:

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return (dispatch, getState) => {
    const {items} = getState().otherReducer;

    dispatch(anotherAction(items));
  }
}

Yanıtlar:


522

Eylem oluşturucularında duruma erişmenin iyi bir fikir olup olmadığı konusunda farklı görüşler vardır:

  • Redux yaratıcısı Dan Abramov, bunun sınırlı olması gerektiğini düşünüyor: "Kabul edilebilir olduğunu düşündüğüm birkaç kullanım durumu, bir istekte bulunmadan önce önbelleğe alınmış verileri kontrol etmek veya kimlik doğrulamanızın doğru olup olmadığını kontrol etmek (başka bir deyişle koşullu bir gönderim yapmak). Ben geçen düşünüyorum verileri gibi state.something.itemsorada bir hata olduğunu ve eğer: bir eylem yaratıcısı kesinlikle bir anti-desen ve bu değişiklik geçmişini gizlenmiş çünkü önerilmez itemsyanlıştır, iz zordur nerede bu yanlış değerler çünkü onlar geliyor bir eyleme yanıt olarak bir redüktör tarafından doğrudan hesaplanmak yerine zaten eylemin bir parçası. Bunu dikkatli bir şekilde yapın. "
  • Mevcut Redux bakıcısı Mark Erikson, ince ve hatta getStategövdede kullanılmaya teşvik edildiğini söylüyor - bu yüzden var . İdiomatic Redux: Thunks, Sagas, Abstraction ve Reusability Üzerine Düşünceler adlı blog yazısında eylem yaratıcılarında devlete erişmenin artılarını ve eksilerini tartışıyor .

Buna ihtiyacınız olduğunu fark ederseniz, önerdiğiniz her iki yaklaşım da iyidir. İlk yaklaşım herhangi bir ara katman yazılımı gerektirmez:

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

Bununla birlikte store, bazı modüllerden dışa aktarılan tekil bir öğe olmasına bağlı olduğunu görebilirsiniz . Uygulamanıza sunucu oluşturmayı eklemeyi zorlaştırdığından bunu önermiyoruz çünkü sunucudaki çoğu durumda istek başına ayrı bir mağazaya sahip olmak isteyeceksiniz . Bu nedenle teknik olarak bu yaklaşım işe yararken bir mağazanın bir modülden dışa aktarılmasını önermiyoruz.

Bu yüzden ikinci yaklaşımı öneriyoruz:

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return (dispatch, getState) => {
    const {items} = getState().otherReducer;

    dispatch(anotherAction(items));
  }
}

Redux Thunk ara katman yazılımı kullanmanız gerekir, ancak hem istemcide hem de sunucuda iyi çalışır. Redux Thunk ve bu durumda neden gerekli olduğu hakkında daha fazla bilgiyi burada bulabilirsiniz .

İdeal olarak, eylemleriniz “şişman” olmamalı ve mümkün olduğunca az bilgi içermelidir, ancak kendi uygulamanızda sizin için en uygun olanı yapmaktan çekinmeyin. Redux SSS dair bilgi eylem yaratıcıları ve düşürücüler arasındaki bölme mantığı ve kullanımı yararlı olabilir zamanlarda getStatebir eylem yaratıcısı içinde .


5
Mağaza bileşenle ilgili veri içerip içermediğine bağlı olarak, bir bileşendeki bir şeyi seçerken bir PUT veya POST tetikleyebilir. Thunk tabanlı eylem oluşturucu yerine PUT / POST seçimi için iş mantığını bileşene koymak daha mı iyi?
vicusbass

3
En iyi uygulama nedir? Şimdi eylem oluşturucumda getState kullandığım benzer bir sorunla karşı karşıyayım. Benim durumumda bir form bekleyen değişiklikler varsa (ve eğer öyleyse bir iletişim kutusu gösteren bir eylem gönderirim) değerleri belirlemek için kullanın.
Arne H.Bitubekk

42
Aksiyon oluşturucudaki mağazadan okumak güzel. Ben öneririm bir seçici kullanmak size tam devlet şekline bağlı kalmamak.
Dan Abramov

2
Mixpanel'e veri göndermek için bir ara katman yazılımı kullanıyorum. Yani eylemin içinde bir meta anahtarım var. Ben durumdan mixpanel farklı değişkenler geçmek gerekir. Onları aksiyon yaratıcılarına yerleştirmek bir anti-desen gibi görünüyor. Bu tür kullanım durumlarını ele almak için en iyi yaklaşım ne olabilir?
Aakash Sigdel

2
Ah adamım! Redux getState
thunk'ın

33

Senaryonuz basit olduğunda kullanabilirsiniz

import store from '../store';

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return {
    type: SOME_ACTION,
    items: store.getState().otherReducer.items,
  }
}

Ancak bazen action creatorçoklu eylemleri tetikleme gereksiniminiz

örneğin REQUEST_LOAD REQUEST_LOAD_SUCCESS REQUEST_LOAD_FAILeylemlere ihtiyaç duyduğunuz zaman uyumsuz istek

export const [REQUEST_LOAD, REQUEST_LOAD_SUCCESS, REQUEST_LOAD_FAIL] = [`REQUEST_LOAD`
    `REQUEST_LOAD_SUCCESS`
    `REQUEST_LOAD_FAIL`
]
export function someAction() {
    return (dispatch, getState) => {
        const {
            items
        } = getState().otherReducer;
        dispatch({
            type: REQUEST_LOAD,
            loading: true
        });
        $.ajax('url', {
            success: (data) => {
                dispatch({
                    type: REQUEST_LOAD_SUCCESS,
                    loading: false,
                    data: data
                });
            },
            error: (error) => {
                dispatch({
                    type: REQUEST_LOAD_FAIL,
                    loading: false,
                    error: error
                });
            }
        })
    }
}

Not: eylem oluşturucudaki işlevi döndürmek için redux-thunk'a ihtiyacınız var


3
Birincisi bitirirken başka bir ajax isteğinin yapılmaması için devletin 'yükleme' durumunun kontrol edilmesi gerekip gerekmediğini sorabilir miyim?
JoeTidee

@JoeTidee Örnekte yükleme durumunun gönderilmesi yapılır. Bu işlemi örneğin düğmeyle yaparsanız, loading === trueorada olup olmadığını kontrol eder ve düğmeyi devre dışı bırakırsınız .
croraf

4

@Bloomca'ya katılıyorum. Mağazadan gereken değerin bir argüman olarak gönderim işlevine geçirilmesi, depoyu dışa aktarmaktan daha kolaydır. Burada bir örnek verdim:

import React from "react";
import {connect} from "react-redux";
import * as actions from '../actions';

class App extends React.Component {

  handleClick(){
    const data = this.props.someStateObject.data;
    this.props.someDispatchFunction(data);
  }

  render(){
    return (
      <div>       
      <div onClick={ this.handleClick.bind(this)}>Click Me!</div>      
      </div>
    );
  }
}


const mapStateToProps = (state) => {
  return { someStateObject: state.someStateObject };
};

const mapDispatchToProps = (dispatch) => {
  return {
    someDispatchFunction:(data) => { dispatch(actions.someDispatchFunction(data))},

  };
}


export default connect(mapStateToProps, mapDispatchToProps)(App);

Bu yöntem oldukça mantıklı.
Ahmet Şimşek

Bunu yapmanın doğru yolu ve nasıl yaptığımı. Aksiyon oluşturucunuzun tüm durumu bilmesine gerek yoktur, yalnızca onunla alakalı olan kısmı.
Ash

3

Mağazadan okumanın o kadar da kötü olmadığını belirtmek isterim - mağazaya göre ne yapılması gerektiğine karar vermek, her şeyi bileşene ve ardından bir parametre olarak iletmekten çok daha uygun olabilir. bir işlev. Dan ile tamamen katılıyorum, sadece müşteri tarafı işleme için kullanacağınızdan% 100 emin değilseniz mağazayı tek ton olarak kullanmamanın çok daha iyi olduğunu düşünüyorum (aksi takdirde hataları izlemek zor görünebilir).

Son zamanlarda redux ayrıntılarıyla başa çıkmak için bir kütüphane oluşturdum ve her şeyi ara katman yazılımına koymanın iyi bir fikir olduğunu düşünüyorum, bu yüzden bir bağımlılık enjeksiyonu olarak her şey var.

Yani, örneğin şöyle görünecek:

import { createSyncTile } from 'redux-tiles';

const someTile = createSyncTile({
  type: ['some', 'tile'],
  fn: ({ params, selectors, getState }) => {
    return {
      data: params.data,
      items: selectors.another.tile(getState())
    };
  },
});

Ancak, gördüğünüz gibi, burada verileri gerçekten değiştirmiyoruz, bu nedenle bu seçiciyi başka bir yerde birleştirmek için başka bir yerde kullanabilmemiz için iyi bir şans var.


1

Bunu çözmenin alternatif bir yolunu sunmak. Bu, uygulamanıza bağlı olarak Dan'ın çözümünden daha iyi veya daha kötü olabilir.

Eylemi 2 ayrı işleve bölerek durumu düşürücülerden eylemlere alabilirsiniz: önce verileri isteyin, sonra veriler üzerinde hareket edin. Bunu kullanarak yapabilirsiniz redux-loop.

İlk olarak 'nazikçe veri isteyin'

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
    return {
        type: SOME_ACTION,
    }
}

Redüktörde, soruyu durdurun ve verileri kullanarak ikinci aşama eylemine verin redux-loop.

import { loop, Cmd } from 'redux-loop';
const initialState = { data: '' }
export default (state=initialState, action) => {
    switch(action.type) {
        case SOME_ACTION: {
            return loop(state, Cmd.action(anotherAction(state.data))
        }
    }
}

Eldeki verilerle, başlangıçta ne istersen yap

export const ANOTHER_ACTION = 'ANOTHER_ACTION';
export function anotherAction(data) {
    return {
        type: ANOTHER_ACTION,
        payload: data,
    }
}

Umarım bu birine yardımcı olur.


0

Burada partiye geç kaldığımı biliyorum, ama buraya devleti eylemlerde kullanma arzumla ilgili görüşler için geldim ve sonra doğru davranış olduğunu düşündüğüm şeyi fark ettiğimde kendimi oluşturdum.

Burada bir seçici benim için en anlamlı olan nokta. Bu isteği gönderen bileşeninize, seçim yoluyla verme zamanı gelip gelmediği söylenmelidir.

export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
  return (dispatch) => {
    dispatch(anotherAction(items));
  }
}

Sızan soyutlamalar gibi hissedebilir, ancak bileşeninizin açıkça bir mesaj göndermesi gerekir ve mesaj yükü ilgili durumu içermelidir. Maalesef sorunuzun somut bir örneği yok çünkü seçicilerin ve eylemlerin “daha ​​iyi bir modelini” bu şekilde çalışabiliriz.


0

Ben en temiz bulmak başka bir alternatif önermek istiyorum, ama gerektirir react-reduxveya simüler bir şey - ben de yol boyunca birkaç fantezi özellikleri kullanıyorum:

// actions.js
export const someAction = (items) => ({
    type: 'SOME_ACTION',
    payload: {items},
});
// Component.jsx
import {connect} from "react-redux";

const Component = ({boundSomeAction}) => (<div
    onClick={boundSomeAction}
/>);

const mapState = ({otherReducer: {items}}) => ({
    items,
});

const mapDispatch = (dispatch) => bindActionCreators({
    someAction,
}, dispatch);

const mergeProps = (mappedState, mappedDispatches) => {
    // you can only use what gets returned here, so you dont have access to `items` and 
    // `someAction` anymore
    return {
        boundSomeAction: () => mappedDispatches.someAction(mappedState.items),
    }
});

export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);
// (with  other mapped state or dispatches) Component.jsx
import {connect} from "react-redux";

const Component = ({boundSomeAction, otherAction, otherMappedState}) => (<div
    onClick={boundSomeAction}
    onSomeOtherEvent={otherAction}
>
    {JSON.stringify(otherMappedState)}
</div>);

const mapState = ({otherReducer: {items}, otherMappedState}) => ({
    items,
    otherMappedState,
});

const mapDispatch = (dispatch) => bindActionCreators({
    someAction,
    otherAction,
}, dispatch);

const mergeProps = (mappedState, mappedDispatches) => {
    const {items, ...remainingMappedState} = mappedState;
    const {someAction, ...remainingMappedDispatch} = mappedDispatch;
    // you can only use what gets returned here, so you dont have access to `items` and 
    // `someAction` anymore
    return {
        boundSomeAction: () => someAction(items),
        ...remainingMappedState,
        ...remainingMappedDispatch,
    }
});

export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);

Bunu yeniden kullanmak isterseniz mapState, başka bir yerde yeniden kullanmak için belirli mapDispatchve mergePropsişlevlere ayırmanız gerekir, ancak bu bağımlılıkları mükemmel bir şekilde netleştirir.

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.