【问题标题】:how to write async redux actions in a non-fake app如何在非伪造应用程序中编写异步 redux 操作
【发布时间】:2016-06-08 14:24:50
【问题描述】:

tl;dr: 我需要一个异步 redux-thunk 操作的示例来展示如何进行异步调用(例如 fetch),并触发状态更新。我还需要看看有人如何将多个此类操作链接在一起,例如:(1)查看用户是否存在于云中,然后(2)如果不存在,则注册它们,然后(3)使用新的用户记录来获取更多数据。


我发现的所有示例都假设 redux 存储可以直接导入到定义操作的模块中。据我了解,这是一种不好的做法:调用组件负责通过this.props.dispatch(来自通过<Provider> 注入的存储)提供对存储的访问。

相反,redux 世界中的每个操作都应该返回一个函数,该函数将接收适当的dispatch;该功能应该完成工作,并返回......一些东西。 Obv,重要的是事物是什么。

根据文档,这是我尝试过的模式,但已证明是失败的。文档中的任何内容都没有明确说明为什么这不起作用,但事实并非如此——因为此操作不会返回承诺。

/**
 * pushes a new user into the cloud; once complete, updates the store with the new user row
 * @param  {hash} user - of .firstName, .lastName
 * @return {promise} resolves with user { userId, firstName, lastName, dateCreated }, or rejects with error
 */
Actions.registerUser = function(user) {
    return function reduxAction(dispatch) {
        return API.createUser(user) // API.createUser just does return fetch(...)
        .then(function onUserRegistered(newUser) {
            return dispatch({
                type: 'ADD_USERS',
                users: [newUser]
            });
        });
    };
};

我有一个响应ADD_USERS 事件的reducer;它将一个或多个用户的传入数组与内存中的用户数组合并。减速器很容易编写。这就是我切换到 redux 的原因:一个商店,纯函数。但是这种笨拙的生意绝对是一场噩梦。

我收到的错误是.then 在Actions.registerUser 上未定义——即Actions.registerUser 没有返回承诺。

我认为问题显然在于我要返回一个函数——reduxAction 函数——但这似乎没有商量余地。在商店拍摄数据的唯一方法是使用提供的dispatch 方法,这意味着我无法返回承诺。

将 onUserRegistered 更改为简单的 invoke 调度然后返回所需的值也不起作用,也不会返回实际的承诺。

请停止。我真的不明白。我不敢相信人们能忍受这一切。


编辑:为了提供一些上下文,这是我认为我应该能够执行的那种动作组合,而这些重击动作令人沮丧:

Actions.bootSetup = function() {
    return dispatch => {
        return Actions.loadUserId() // looks for userId in local storage, or generates a new value
        .then(Actions.storeUserId) // pushes userId into local storage
        .then((userId) => {
            return Actions.fetchUsers(userId) // fetches the user, by id, from the cloud
            .then((user) => {
                // if necessary, pushes the user into the cloud, too
                return user || Actions.postUser({ userId: userId, firstName: 'auto-registered', lastName: 'tbd'}); 
            });
        })
        .then((user) => {
            console.log(`boot sequence complete with user `, user);
            return dispatch({ type: 'ADD_OWNER', user });
        });
    };
};

我希望Actions.storeUserId 和Actions.fetchUsers 除了返回以我选择的值解析的承诺之外,还会将数据发送到存储区作为副作用。我认为调度正在发生,但链条中断了,因为这些操作都没有返回承诺 - 它们返回普通函数。

这不仅看起来比 Flux 糟糕得多,而且似乎难以理解。我不敢相信,为了将应用程序状态整合到一个单一的减少存储中,所有这些疯狂都是必要的。

是的——我已经尝试了新版本的flux,它带有ReducerStore,但是它对与react-native不兼容的CSS库有一些不适当的依赖。项目维护者表示他们不打算解决这个问题。我猜他们的状态容器依赖于 CSS 功能。


编辑:我的商店

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import Reducers from './reducers';

const createStoreWithMiddleWare = applyMiddleware(thunk)(createStore);


export const initialState = {
    users: [] // will hold array of user objects
};

const store = createStoreWithMiddleWare(Reducers);

export default store;

编辑:这是调用代码。这是根级别的 react-native 组件。

// index.ios.js
import Store from './store';

class myApp extends Component {

    componentDidMount() {
        Store.dispatch(Actions.bootSetup())
        .then(() => {
            console.log('*** boot complete ***');
        });
    }

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

}

我的假设是Store.dispatch 需要一个函数,并为它提供对 store 调度方法的引用。

【问题讨论】:

  • 您能说明您是如何在您的createStore 中连接thunk 中间件的吗?
  • 能否也显示调用代码?
  • 如果您还没有阅读文档,我强烈建议您阅读。具体来说,this page 会回答您的问题。

标签: reactjs redux redux-thunk


【解决方案1】:

我马上就能看出一个错误

Actions.bootSetup = function() {
    return dispatch => {
        return Actions.loadUserId()

您没有正确链接 thunk 操作。如果您的操作返回一个函数,您需要将调度传递给该操作。

看看这个action creator(这是一个功能齐全的真实世界应用程序,随意四处逛逛),看看第9行,这里调用了loginUser。

export function changePassword(credentials) {
    return (dispatch, getState) => {
        dispatch(changePasswordStart(credentials))
        return Firebase.changePassword(credentials)
            .then(() => {
                return logout()
            })
            .then(() => {
                return loginUser(credentials.email, credentials.newPassword)(dispatch)
            })
            .then(() => {
                    dispatch(changePasswordSuccess(credentials))
                    toast.success('Password successfully changed')
            }).catch(error => {
                    dispatch(changePasswordError(error.code))
                    toast.error('An error occured changing your password: ' + error.code)
            })
    }
}

因为loginUser 也是一个thunk action,所以它需要将dispatch 传递给调用它的结果。如果您考虑一下,这是有道理的:thunk 不做任何事情,它只是创建一个函数。您需要调用它返回的函数以使其执行操作。由于它返回的函数将dispatch 作为参数,因此您也需要将其传入。

一旦完成,从 thunk 动作返回一个承诺将起作用。事实上,我上面给出的例子就是这样做的。 loginUser 返回一个承诺,changePassword 也是如此。两者都是thenables。

您的代码可能需要如下所示(虽然我不确定,但我没有调用这些操作)

Actions.bootSetup = function() {
    return dispatch => {
        return Actions.loadUserId()(dispatch) // pass dispatch to the thunk
        .then(() => Actions.storeUserId(dispatch)) // pass dispatch to the thunk
        .then((userId) => {
            return Actions.fetchUsers(userId)(dispatch) // pass dispatch to the thunk
            .then((user) => {
                // pass dispatch to the thunk
                return user || Actions.postUser({ userId: userId, firstName: 'auto-registered', lastName: 'tbd'})(dispatch); 
            });
        })
        .then((user) => {
            console.log(`boot sequence complete with user `, user);
            return dispatch({ type: 'ADD_OWNER', user });
        });
    };
};

【讨论】:

  • 这正是问题所在。它还向我证实了return dispatch(...) 是不必要的——就像在 Flux 中一样,return Dispatcher.dispatch(...) 是一个副作用,所以你仍然可以返回对操作有意义的任何数据。跨度>
  • 在这种情况下返回 thunk 承诺会很有用:如果他们调用的操作没有返回 thenable,则其他操作无法正确链接。我建议始终从 thunk 操作中返回承诺
  • 我不确定我理解你所说的“thunk promise”是什么意思。我同意如果可以依赖所有这些方法来返回thenables,那将很有用; dispatch 是否返回承诺?
  • 不,dispatch 不返回承诺。 thunk promise 是boolSetup 或postUser 返回的函数。这些应该return dispatch =&gt; {}
猜你喜欢
  • 2013-03-12
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 2016-06-02
  • 1970-01-01
  • 2017-01-19
  • 2020-04-23
  • 1970-01-01
相关资源
最近更新 更多