如何处理 Redux 中的依赖动作创建者?
How to handle dependent action creators in Redux?
假设我有一个 REST API 来编辑用户配置文件。
这里涉及两个方法
- 一种更新用户资料的方法;
- 获取当前配置文件完成分数的方法。
当然,当用户更新他的个人资料时,我想获取完成分数。
这是我(天真的?)使用 redux
和 redux-thunk
动作创作者
的方法
function updateProfileAction(userId, profile) {
return function (dispatch) {
dispatch("PROFILE_UPDATE", /*...*/)
api.updateProfile(userId, profile)
.then(repsonse => dispatch("PROFILE_UPDATE_SUCCESS", /*...*/))
// ▼ triggers the action that fetch the completion score
// this is the line that bugs me
.then(() => fetchProfileCompletionAction(userId))
}
}
这条线让我烦恼的是什么:
- 它打破了单一职责原则(
udpateProfileAction
突然负责也抓取完成);
- 它不是分形(如果我想添加一个依赖于配置文件更新的功能,我需要再次修改那段代码。
例如,如果由于某种原因我不想再显示完成(假设我删除了显示它的组件)并且忘记删除这行代码,则 fetchCompletion
将是触发但永远不会在图形组件中使用)。
我在想,也许在消费completion的组件中,此时我可以订阅store和dispatch
。这将使我能够以 更具反应性的方式进行编码
function Completion(props) {
props.store.subscribe(state => {
//check if profile has changed
dispatch(fetchProfileCompletionAction(userId))
})
return <div> {props.completion} </div>
}
这是个好主意吗?有一个更好的方法吗?另外我想检查相关状态是否已经改变对于这个解决方案来说并不是很简单。
就个人而言,我更喜欢您当前的解决方案。
我是这样想的:
- 动作代表系统中发生的事情
- 状态可能会因操作而改变
- 组件订阅它们呈现的状态的变化
有了这个理念,如果引发了一个没有 reducer 正在寻找的额外动作,也没关系。如果某些状态更改没有任何组件将呈现,则没有关系。
根据你的第二个建议,正如你所建议的,很难确定状态是否发生了变化,因为你只有当前状态,没有以前的状态,才能确定这一点。
如果你确实想做这样的事情,你可以使用 componentWillReceiveProps
并使用 react-redux 连接组件,这样状态变化通过 props 进入,例如:
class ProfileSuccessNotification extends React.Component {
componentWillReceiveProps(nextProps) {
if (!this.props.success && nextProps.success) {
this.props.showNotification(nextProps.userId)
}
}
render() {
return this.props.show ? <p>{this.props.userId} fetched successfully!</p> : null
}
}
// map from where ever in your state
const mapStateToProps = (state) => ({
userId: state.profile.userId,
success: state.profile.loaded,
show: state.profileNotification.show
})
const mapDispatchToProps = (dispatch) => ({
showNotification: (userId) => dispatch(fetchProfileCompletionAction(userId))
})
export default connect(mapStateToProps, mapDispatchToProps)(ProfileSuccessNotification)
当然,你也可以让多个reducer对同一个动作做出反应。我不确定 PROFILE_UPDATE_SUCCESS
和 fetchProfileCompletionAction
引发的动作之间有什么区别,所以也许你只想在 PROFILE_UPDATE_SUCCESS
引发时更新 2 个状态,否定根本不需要链接第二个动作。
因此,经过一些测试和研究后,我尝试使用 redux-cycles
来替代 redux-thunk
,它可以很好地处理这类问题。
它允许我订阅正在更改的配置文件,并在更新配置文件时向服务器请求完成。这比我在最初的问题中所做的语义更正确。
这是代码的样子
function main(sources) {
const updateProfileRequest = sources.ACTION
.filter(action => action.type === "UPDATE_PROFILE")
.map(action => ({ /* update profile request object */))
const profileUpdateSuccess$ = sources.HTTP
.select("profileUpdate")
.flatten()
// when a profile request is successful, I want to warn the store
.map(response => ({ type: "UPDATE_PROFILE_SUCCESS", profile: response.body })
const fetchCompletionRequest$ = sources.STATE
.map(state => state.profile)
// whenever the state changes, I want to fetch the completion
.compose(dropRepeats())
.mapTo({ /* fetch profile request object */ })
return {
HTTP: xs.merge(udpateProfileRequest$, fetchCompletionRequest$),
ACTION: profileUpdateSuccess$
}
}
语义与redux-thunk
在我的第一次尝试中,代码的语义是:
当用户请求更新他的个人资料时:向服务器发送更新请求AND当响应到达时调度profile_update_success
AND 发送获取新完成值的请求
这就是单个组件的职责。
语义与redux-cycles
现在,redux-cycles
是我的代码的意思:
当用户请求更新他的个人资料时:向服务器发送更新请求
更新配置文件响应到达时:发送 UPDATE_PROFILE_SUCCESS
操作
店铺资料更新时:发送请求获取新完成
这三个动作是完全分离的,并且具有独立的语义。
注意第二步是 "When an update profile response arrives" 而不是 "When the update profile response" :)
所以最后,我不是在解决非分形问题,但我认为这是redux
的本质,所以我没有太多可以做到这一点,但至少我有更多的反应系统。
假设我有一个 REST API 来编辑用户配置文件。 这里涉及两个方法
- 一种更新用户资料的方法;
- 获取当前配置文件完成分数的方法。
当然,当用户更新他的个人资料时,我想获取完成分数。
这是我(天真的?)使用 redux
和 redux-thunk
动作创作者
function updateProfileAction(userId, profile) {
return function (dispatch) {
dispatch("PROFILE_UPDATE", /*...*/)
api.updateProfile(userId, profile)
.then(repsonse => dispatch("PROFILE_UPDATE_SUCCESS", /*...*/))
// ▼ triggers the action that fetch the completion score
// this is the line that bugs me
.then(() => fetchProfileCompletionAction(userId))
}
}
这条线让我烦恼的是什么:
- 它打破了单一职责原则(
udpateProfileAction
突然负责也抓取完成); - 它不是分形(如果我想添加一个依赖于配置文件更新的功能,我需要再次修改那段代码。
例如,如果由于某种原因我不想再显示完成(假设我删除了显示它的组件)并且忘记删除这行代码,则 fetchCompletion
将是触发但永远不会在图形组件中使用)。
我在想,也许在消费completion的组件中,此时我可以订阅store和dispatch
。这将使我能够以 更具反应性的方式进行编码
function Completion(props) {
props.store.subscribe(state => {
//check if profile has changed
dispatch(fetchProfileCompletionAction(userId))
})
return <div> {props.completion} </div>
}
这是个好主意吗?有一个更好的方法吗?另外我想检查相关状态是否已经改变对于这个解决方案来说并不是很简单。
就个人而言,我更喜欢您当前的解决方案。
我是这样想的:
- 动作代表系统中发生的事情
- 状态可能会因操作而改变
- 组件订阅它们呈现的状态的变化
有了这个理念,如果引发了一个没有 reducer 正在寻找的额外动作,也没关系。如果某些状态更改没有任何组件将呈现,则没有关系。
根据你的第二个建议,正如你所建议的,很难确定状态是否发生了变化,因为你只有当前状态,没有以前的状态,才能确定这一点。
如果你确实想做这样的事情,你可以使用 componentWillReceiveProps
并使用 react-redux 连接组件,这样状态变化通过 props 进入,例如:
class ProfileSuccessNotification extends React.Component {
componentWillReceiveProps(nextProps) {
if (!this.props.success && nextProps.success) {
this.props.showNotification(nextProps.userId)
}
}
render() {
return this.props.show ? <p>{this.props.userId} fetched successfully!</p> : null
}
}
// map from where ever in your state
const mapStateToProps = (state) => ({
userId: state.profile.userId,
success: state.profile.loaded,
show: state.profileNotification.show
})
const mapDispatchToProps = (dispatch) => ({
showNotification: (userId) => dispatch(fetchProfileCompletionAction(userId))
})
export default connect(mapStateToProps, mapDispatchToProps)(ProfileSuccessNotification)
当然,你也可以让多个reducer对同一个动作做出反应。我不确定 PROFILE_UPDATE_SUCCESS
和 fetchProfileCompletionAction
引发的动作之间有什么区别,所以也许你只想在 PROFILE_UPDATE_SUCCESS
引发时更新 2 个状态,否定根本不需要链接第二个动作。
因此,经过一些测试和研究后,我尝试使用 redux-cycles
来替代 redux-thunk
,它可以很好地处理这类问题。
它允许我订阅正在更改的配置文件,并在更新配置文件时向服务器请求完成。这比我在最初的问题中所做的语义更正确。
这是代码的样子
function main(sources) {
const updateProfileRequest = sources.ACTION
.filter(action => action.type === "UPDATE_PROFILE")
.map(action => ({ /* update profile request object */))
const profileUpdateSuccess$ = sources.HTTP
.select("profileUpdate")
.flatten()
// when a profile request is successful, I want to warn the store
.map(response => ({ type: "UPDATE_PROFILE_SUCCESS", profile: response.body })
const fetchCompletionRequest$ = sources.STATE
.map(state => state.profile)
// whenever the state changes, I want to fetch the completion
.compose(dropRepeats())
.mapTo({ /* fetch profile request object */ })
return {
HTTP: xs.merge(udpateProfileRequest$, fetchCompletionRequest$),
ACTION: profileUpdateSuccess$
}
}
语义与redux-thunk
在我的第一次尝试中,代码的语义是:
当用户请求更新他的个人资料时:向服务器发送更新请求AND当响应到达时调度profile_update_success
AND 发送获取新完成值的请求
这就是单个组件的职责。
语义与redux-cycles
现在,redux-cycles
是我的代码的意思:
当用户请求更新他的个人资料时:向服务器发送更新请求
更新配置文件响应到达时:发送 UPDATE_PROFILE_SUCCESS
操作
店铺资料更新时:发送请求获取新完成
这三个动作是完全分离的,并且具有独立的语义。
注意第二步是 "When an update profile response arrives" 而不是 "When the update profile response" :)
所以最后,我不是在解决非分形问题,但我认为这是redux
的本质,所以我没有太多可以做到这一点,但至少我有更多的反应系统。