Redux: разрешены ли только синхронные вызовы из функций-редукторов?

У меня есть приложение responseJs, и прямо сейчас я изучаю Redux, чтобы использовать его как реализацию Flux.

Я создал магазин, и я создал свою первую функцию-редуктор, но теперь у меня есть несколько вопросов, которые приходят мне в голову, пожалуйста, помогите мне разобраться.

Как видите, у меня есть действие под названием «FIND_PRODUCTS», которое в основном извлекает данные из серверной службы. Чтобы вызвать эту внутреннюю службу, я использую в основном асинхронный вызов ajax, поэтому в основном проблема, с которой я сталкиваюсь, заключается в том, что состояние возвращается из функции редуктора до завершения моего внутреннего вызова, поэтому тогда состояние не обновляется правильно и подписчики на магазин получает неверные данные. Эта проблема решается, если я переключаюсь на синхронный вызов, но тогда первое предупреждение, которое я получаю, заключается в том, что следует избегать синхронного вызова, потому что это может снизить качество обслуживания (производительность) пользователя.

Итак, мой вопрос, можем ли мы получать данные только синхронно из функции редуктора? Должна ли выборка данных происходить в функции редуктора или есть другой способ сделать это? Если так, то, что это?

Хорошо ли масштабируется эта модель сокращения наличия единого дерева объектов для поддержания состояния с большими приложениями? Если у меня есть 1000 действий, переключатель в моей функции редуктора будет огромным! Как этого избежать?

Спасибо!!

const initialState = {
availableLocales: [{text: 'En'}, {text: 'Es'}, {text: 'Fr'}],
selectedLocale: 'En',
translations: i18n.getTranslations(),
products: []
};


const reducer = (state = initialState, action = {type: 'NONE'})=> {

//To make the reducer a pure function
deepFreeze(state);
deepFreeze(action);
switch (action.type) {
    case 'SWITCH_LOCALE':
        let newState = Object.assign({}, state, {
            selectedLocale: action.locale,
            translations: i18n.getTranslations(action.locale)
        });
        return newState;
    case 'FIND_PRODUCTS':
        let newState = Object.assign({}, state, {
            products:ProductHelper().findProductsByProductType(action.productType)
        });
        return newState;

    default:
        return state
}
return state;
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
const store = createStore(reducer);

// You can subscribe to the updates manually, or use bindings to your view  layer.
store.subscribe(() =>
    console.log(store.getState())
);

export default store;

person fgonzalez    schedule 02.03.2016    source источник
comment
Не может компонент вызвать асинхронную функцию findProduct в методе componentWillMount?   -  person Raulucco    schedule 02.03.2016
comment
Конечно, но тогда зачем нам магазин? Я думал, что весь смысл этого в том, чтобы переместить состояние из компонентов в хранилище ..   -  person fgonzalez    schedule 02.03.2016


Ответы (4)


Учти это:

Создайте файл actions.js и экспортируйте функции действий следующим образом:

import * as types from '../constants/action_types';
import * as api from '../utils/api'

export function something1(someId){

    return (dispatch) => {

        dispatch({type: `${types.SOMETHING1}_PENDING`});

        api.getSomething(someId)

            .then((res) => {
                dispatch({
                    type: `${types.SOMETHING1}_SUCCEEDED`,
                    somethings: res.body
                });

            .catch((err) => {

                dispatch({
                    type: `${types.SOMETHING1}_FAILED`,
                    errors: err.body
                })

            });
    }
} 

export function something2(someOtherId){

    return (dispatch) => {

        dispatch({type: `${types.SOMETHING2}_PENDING`});

        api.getSomething2(someOtherId)

            .then((res) => {
                dispatch({
                    type: `${types.SOMETHING2}_SUCCEEDED`,
                    otherThings: res.body
                });

            .catch((err) => {

                dispatch({
                    type: `${types.SOMETHING2}_FAILED`,
                    errors: err.body
                })

            });
    }
} 

Тогда состояние меняется только тогда, когда у вас есть данные

Затем разделите ваши редукторы в отдельные файлы и создайте один файл для их экспорта, например: reducers / index.js:

export { default as reducer1 } from './reducer1';
export { default as reducer2 } from './reducer2';
export { default as reducer3 } from './reducer3';
export { default as reducer4 } from './reducer4';

Затем настройте свой магазин следующим образом:

configure_store.js

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import * as reducers from '../reducers';

const rootReducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);

export default function configureStore(initialState) {
    return createStoreWithMiddleware(rootReducer, initialState);
}

Наконец, добавьте это в свой корень:

import configureStore from '../store/configure_store';

const store = configureStore();

class Root extends Component {

    render() {
        return (
            ...
            <Provider store={ store } >
            ...
            </Provider>
        );
    }
}

export default Root;
person Sagi Medina    schedule 02.03.2016

Во-первых, вы НЕ МОЖЕТЕ получить данные в reducer, потому что они должны быть чистыми по определению redux. Вы должны создать создатель действия, который будет асинхронно извлекать данные и передавать их редуктору. Действия МОГУТ быть нечистыми.

Здесь вы можете прочитать больше http://redux.js.org/docs/advanced/AsyncActions.html

Также вы можете использовать промежуточное ПО, такое как redux-thunk, чтобы упростить это. https://github.com/gaearon/redux-thunk


Что касается второго вопроса, в вашем приложении может быть более одного редуктора. и затем объединить их с combineReducers(...) функцией http://redux.js.org/docs/basics/Reducers.html

person deathmood    schedule 02.03.2016

Как сказано в документации по редукции, редукторы должны быть чистыми функциями, поэтому этого не должно быть. запросы ajax.

Лучший способ сделать это - использовать промежуточное ПО redux-thunk, которое позволяет вызывать dispatch несколько раз в одно действие.

Итак, в вашем примере вы делаете что-то вроде этого:

// definition of action creator
function loadProducts(productType) {
  return {type: 'FIND_PRODUCTS', productType: productType}
}

...
// calling dispatch of your action
dispatch(loadProducts(productType));

Но с redux-thunk ваш создатель действий будет примерно таким:

function loadProducts(productType) {
  return function(dispatch){
    dispatch({type: 'FIND_PRODUCT_STARTED'});
    // I don'h know how findProductsByProductType works, but I assume it returns Promise
    ProductHelper().findProductsByProductType(productType).then(function(products){
      dispatch({type: 'FIND_PRODUCT_DONE', products: products});
    });
  }
}

И ваш редуктор станет чистой функцией:

...
case 'FIND_PRODUCTS_DONE':
  let newState = Object.assign({}, state, {
      products: action.products,
  });
  return newState;
...

В этом случае вы также можете обрабатывать состояние загрузки, то есть установить флаг loading в своем состоянии в значение true, когда action.type равно FIND_PRODUCT_STARTED.

В моем примере я предполагаю, что findProductsByProductType возвращает Promise. В этом случае вы даже можете использовать redux-prom-middleware без redux-thunk, оно будет делаю всю работу за вас:

function loadProducts(productType) {
  return {
    type: 'FIND_PRODUCT',
    payload: {
      promise: ProductHelper().findProductsByProductType(productType)
    }
  }
}
person Alexandr Subbotin    schedule 02.03.2016

Вы не должны использовать ProductHelper () в редукторе для запроса данных.

Вместо этого вы должны использовать создателя действия для отправки действия, которое запрашивает данные из вашего API. Ваше промежуточное программное обеспечение API вернет обещание, что по завершении отправит намерение действия с полезной нагрузкой для вашего редуктора, чтобы он потреблял и возвращал следующее состояние.

Я рекомендую вам посмотреть Redux Thunk и промежуточное ПО Redux API

person Mark    schedule 02.03.2016