如何在非假应用程序中编写异步 redux 操作
how to write async redux actions in a non-fake app
tl;dr: 我需要一个异步 redux-thunk 操作的示例,展示如何进行异步调用(例如 fetch
),并触发状态更新。我还需要了解某人如何将多个此类操作链接在一起,例如:(1) 查看用户是否存在于云中,然后 (2) 如果不存在,则注册它们,然后 (3) 使用新的用户记录来获取更多数据。
我找到的所有示例都假设可以将 redux 存储直接导入到定义操作的模块中。据我了解,这是一种不好的做法:调用组件负责通过 this.props.dispatch
(来自通过 <Provider>
注入的商店)提供对商店的访问。
相反,redux 世界中的每个动作都应该 return 一个将接收适当 dispatch
的函数;该功能应该完成工作,并且 return... 一些东西。 Obv,重要的是东西是什么。
这是我根据文档尝试过的模式,但已被证明是失败的。文档中没有任何内容明确说明为什么这行不通,但它行不通——因为此操作不是 return 承诺。
/**
* 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
事件的减速器;它将传入的一个或多个用户数组与内存中已有的用户数组合并。减速器很容易编写。这就是我转向 redux 的原因:一个商店,纯功能。但这个 thunk 业务绝对是一场噩梦。
我收到的错误是 .then
在 Actions.registerUser
上未定义——即 Actions.registerUser
不是 return 承诺。
我认为问题显然是我正在 return 函数——reduxAction
函数——但这似乎没有商量余地。去店里拍数据只能用提供的dispatch
方法,那就是我不能return承诺。
将 onUserRegistered
更改为简单地 调用 调度,然后 return 所需的值也不起作用,也没有 return 一个实际的承诺。
PLZ HALP。我真的不明白。我不敢相信人们能忍受这一切。
编辑:为了提供一些背景信息,这里是我认为我应该能够执行的那种动作组合,而这些重击动作令人沮丧:
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
除了 returning 承诺以我选择的值解决之外,还将数据作为副作用发送到商店.我认为调度正在发生,但链条中断了,因为 none 这些操作 return 承诺 - 它们 return 普通函数。
这不仅看起来比Flux差很多,而且似乎无法理解。我无法相信所有这些疯狂都是为了将应用程序状态整合到一个单一的还原存储中。
是的——我已经尝试了新版本的 flux,它有 ReducerStore,但它对 CSS 库有一些不适当的依赖,这些库与 react-native 不兼容。项目维护者表示他们不打算解决这个问题。我猜他们的状态容器依赖于 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
需要一个函数,并为其提供对商店调度方法的引用。
我一眼就能看出一个错误
Actions.bootSetup = function() {
return dispatch => {
return Actions.loadUserId()
您没有正确链接 thunk 操作。如果您的操作 returns 是一个函数,您需要将调度传递给该操作。
看看这个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动作,它需要将dispatch传递给调用它的结果。如果您考虑一下,这是有道理的:thunk 什么都不做,它只是创建一个函数。您需要调用函数 it returns 来让它执行操作。由于函数 it returns 将 dispatch
作为参数,因此您也需要将其传入。
完成后,从 thunk 操作返回一个 promise 就可以了。事实上,我上面给出的例子正是这样做的。 loginUser
returns 承诺,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 });
});
};
};
tl;dr: 我需要一个异步 redux-thunk 操作的示例,展示如何进行异步调用(例如 fetch
),并触发状态更新。我还需要了解某人如何将多个此类操作链接在一起,例如:(1) 查看用户是否存在于云中,然后 (2) 如果不存在,则注册它们,然后 (3) 使用新的用户记录来获取更多数据。
我找到的所有示例都假设可以将 redux 存储直接导入到定义操作的模块中。据我了解,这是一种不好的做法:调用组件负责通过 this.props.dispatch
(来自通过 <Provider>
注入的商店)提供对商店的访问。
相反,redux 世界中的每个动作都应该 return 一个将接收适当 dispatch
的函数;该功能应该完成工作,并且 return... 一些东西。 Obv,重要的是东西是什么。
这是我根据文档尝试过的模式,但已被证明是失败的。文档中没有任何内容明确说明为什么这行不通,但它行不通——因为此操作不是 return 承诺。
/**
* 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
事件的减速器;它将传入的一个或多个用户数组与内存中已有的用户数组合并。减速器很容易编写。这就是我转向 redux 的原因:一个商店,纯功能。但这个 thunk 业务绝对是一场噩梦。
我收到的错误是 .then
在 Actions.registerUser
上未定义——即 Actions.registerUser
不是 return 承诺。
我认为问题显然是我正在 return 函数——reduxAction
函数——但这似乎没有商量余地。去店里拍数据只能用提供的dispatch
方法,那就是我不能return承诺。
将 onUserRegistered
更改为简单地 调用 调度,然后 return 所需的值也不起作用,也没有 return 一个实际的承诺。
PLZ HALP。我真的不明白。我不敢相信人们能忍受这一切。
编辑:为了提供一些背景信息,这里是我认为我应该能够执行的那种动作组合,而这些重击动作令人沮丧:
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
除了 returning 承诺以我选择的值解决之外,还将数据作为副作用发送到商店.我认为调度正在发生,但链条中断了,因为 none 这些操作 return 承诺 - 它们 return 普通函数。
这不仅看起来比Flux差很多,而且似乎无法理解。我无法相信所有这些疯狂都是为了将应用程序状态整合到一个单一的还原存储中。
是的——我已经尝试了新版本的 flux,它有 ReducerStore,但它对 CSS 库有一些不适当的依赖,这些库与 react-native 不兼容。项目维护者表示他们不打算解决这个问题。我猜他们的状态容器依赖于 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
需要一个函数,并为其提供对商店调度方法的引用。
我一眼就能看出一个错误
Actions.bootSetup = function() {
return dispatch => {
return Actions.loadUserId()
您没有正确链接 thunk 操作。如果您的操作 returns 是一个函数,您需要将调度传递给该操作。
看看这个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动作,它需要将dispatch传递给调用它的结果。如果您考虑一下,这是有道理的:thunk 什么都不做,它只是创建一个函数。您需要调用函数 it returns 来让它执行操作。由于函数 it returns 将 dispatch
作为参数,因此您也需要将其传入。
完成后,从 thunk 操作返回一个 promise 就可以了。事实上,我上面给出的例子正是这样做的。 loginUser
returns 承诺,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 });
});
};
};