【问题标题】:Redux: Are only synchronous calls allowed from reducer functions?Redux:是否只允许来自 reducer 函数的同步调用?
【发布时间】:2016-03-02 12:28:19
【问题描述】:

我有一个 reactJs 应用程序,现在我正在学习 Redux 以将其用作 Flux 实现。

我已经创建了一个 store,我已经创建了我的第一个 reducer 函数,但是现在我有一些问题浮现在我的脑海中,请帮助我理解。

如您所见,我有一个名为“FIND_PRODUCTS”的操作,它基本上是从后端服务获取数据。要调用这个后端服务,我基本上使用异步 ajax 调用,所以基本上我面临的问题是在我的后端调用完成之前从 reducer 函数返回状态,所以状态没有正确更新,订阅者商店收到不正确的数据。如果我切换到同步调用,这个问题就解决了,但是,我得到的第一个警告是应该避免同步调用,因为它可能会降低用户的体验(性能)。

所以我的问题是,我们只能从 reducer 函数中同步获取数据吗? 获取数据应该发生在 reducer 函数中还是有其他方法可以做到这一点?如果有,是什么?

这种使用单个对象树来维护状态的 redux 模型是否适用于大型应用程序?如果我有 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;

【问题讨论】:

  • 组件不能调用componentWillMount方法上的findProduct异步函数吗?
  • 当然,但是我们需要商店做什么?我认为这样做的全部目的是将状态从组件转移到商店..

标签: javascript reactjs redux react-redux


【解决方案1】:

考虑一下:

创建actions.js文件并像这样导出action函数:

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
                })

            });
    }
} 

那么状态只有在你有数据时才会改变

接下来将你的 reducer 分离到单独的文件中并创建一个文件以将它们全部导出 像这样的 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;

【讨论】:

    【解决方案2】:

    首先,你不能在 reducer 中获取数据,因为它需要通过 redux 定义是纯的。您应该创建动作创建器,它将异步获取数据并将其传递给减速器。操作可能不纯。

    在这里你可以阅读更多http://redux.js.org/docs/advanced/AsyncActions.html

    你也可以使用像redux-thunk 这样的中间件来简化它。 https://github.com/gaearon/redux-thunk


    至于第二个问题,你的应用中可以有多个 reducer。而不是将它们与combineReducers(...)函数http://redux.js.org/docs/basics/Reducers.html结合起来

    【讨论】:

      【解决方案3】:

      正如redux documentation 所说,reducers 应该是纯函数,所以它不应该做 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});
          });
        }
      }
      

      而你的 reducer 会变成纯函数:

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

      在这种情况下,您还可以处理加载状态,即当 action.type 为 FIND_PRODUCT_STARTED 时,将状态中的 loading 标志设置为 true。

      在我的示例中,我假设 findProductsByProductType 返回 Promise。在这种情况下,您甚至可以在没有 redux-thunk 的情况下使用 redux-promise-middleware,它会为您完成所有工作:

      function loadProducts(productType) {
        return {
          type: 'FIND_PRODUCT',
          payload: {
            promise: ProductHelper().findProductsByProductType(productType)
          }
        }
      }
      

      【讨论】:

        【解决方案4】:

        您不应在 reducer 中使用 ProductHelper() 来请求数据。

        相反,您应该使用操作创建器来调度从您的 API 请求数据的操作。您的 API 中间件将返回一个承诺,在完成时将分派一个带有有效负载的操作意图,供您的减速器使用并返回下一个状态。

        我建议你看看Redux Thunk 和Redux API middleware

        【讨论】:

          猜你喜欢
          • 2020-12-13
          • 1970-01-01
          • 1970-01-01
          • 2015-02-20
          • 2010-10-10
          • 2019-03-19
          • 2017-08-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多