3

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

我创建了一个商店,并创建了我的第一个减速器功能,但现在我有一些问题浮现在我的脑海中,请帮助我理解。

如您所见,我有一个名为“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;
4

4 回答 4

5

考虑一下:

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

            });
    }
} 

然后状态只有在你有数据时才会改变

接下来将你的 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;
于 2016-03-02T13:34:46.137 回答
3

首先,你不能在 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结合起来

于 2016-03-02T13:12:21.297 回答
2

正如redux 文档所说,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;
...

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

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

function loadProducts(productType) {
  return {
    type: 'FIND_PRODUCT',
    payload: {
      promise: ProductHelper().findProductsByProductType(productType)
    }
  }
}
于 2016-03-02T13:20:52.757 回答
1

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

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

我推荐你看看Redux ThunkRedux API 中间件

于 2016-03-02T13:14:01.190 回答