所以我正在使用Redux Saga在React中实现一个应用程序,我有点担心我的特定用例的信息很少,因为它看起来并不奇怪。很可能我使用了错误的术语或以错误的方式思考问题,因为我对React / Redux很新。无论如何,我一直在努力谷歌解决这个问题,并希望得到一些比我更有经验的人的见解。
我的应用程序状态上有一个userSettings
属性,用于管理登录用户的一些配置选项。在应用程序的某一点上,用户可以翻转开关以禁用“一目了然”仪表板小部件的显示,我需要将此信息传递给后端API以更新其在数据库中的设置信息,然后根据此后端更新是否成功更新状态。
我目前的代码对于所有用户设置更新都有一个主要的传奇,我打算通过更具体的传奇来获取此设置,因此:
Dashboard.js
function mapStateToProps(state) {
const { userSettings } = state;
return { userSettings };
}
...
class Dashboard extends Component {
...
...
hasDashboardAtAGlanceHiddenToggle() {
const { dispatch, userSettings } = this.props;
dispatch(setHasDashboardAtAGlanceHidden(!userSettings.hasDashboardAtAGlanceHidden));
}
}
export default connect(mapStateToProps)(Dashboard);
updateUserSettingsSaga.js
import { take, put, call } from 'redux-saga/effects';
import axios from 'axios';
import {
UPDATE_USER_SETTINGS,
SET_HAS_DASHBOARD_AT_A_GLANCE_HIDDEN,
updateUserSettings,
updatedUserSettingsSuccess
} from '../../actions';
export function* setHasDashboardAtAGlanceHiddenSaga() {
const action = yield take(SET_HAS_DASHBOARD_AT_A_GLANCE_HIDDEN);
const newValue = action.data;
//QUESTION HERE -- how to get full object to pass to updateUserSettings
yield put(updateUserSettings(stateObjectWithNewValuePopulated));
}
export default function* updateUserSettingsSaga(data) {
yield take(UPDATE_USER_SETTINGS);
try {
const response = yield call(axios.put, 'http://localhost:3001/settings', data);
yield put(updatedUserSettingsSuccess(response.data));
} catch (e) {
yield put(updatedUserSettingsFailure());
}
}
我的问题,如代码中所述,我不确定将更新后的值合并到状态的逻辑应该在何处/如何发生。尽可能接近,我有三个选择:
在分派初始操作之前,在组件中构建更新状态,即:
hasDashboardAtAGlanceHiddenToggle() {
const { dispatch, userSettings } = this.props;
const newState = Object.assign({}, userSettings , {
hasDashboardAtAGlanceHidden: !userSettings.hasDashboardAtAGlanceHidden
});
dispatch(setHasDashboardAtAGlanceHidden(userSettings));
}
}
使用redux-saga的select
效果并在更具体的初始传奇中构建完整的状态对象,即:
export function* setHasDashboardAtAGlanceHiddenSaga() {
const action = yield take(SET_HAS_DASHBOARD_AT_A_GLANCE_HIDDEN);
const newValue = action.data;
const existingState = select(state => state.userSettings);
const updatedState = Object.assign({}, existingState, {
hasDashboardAtAGlanceHidden: newValue
});
yield put(updateUserSettings(updatedState));
}
在更新之前检索服务器的用户设置对象副本,即:
export default function* updateUserSettingsSaga() {
const action = yield take(UPDATE_USER_SETTINGS);
try {
const current = yield call(axios.get, 'http://localhost:3001/settings');
const newState = Object.assign({}, current.data, action.data);
const response = yield call(axios.put, 'http://localhost:3001/settings', newState);
yield put(updatedUserSettingsSuccess(response.data));
} catch (e) {
yield put(updatedUserSettingsFailure());
}
}
所有这些(我认为)都可以作为选项,但我并不清楚在Redux Saga的背景下哪种惯用/接受/可取的方法,并且有一些令人眼花缭乱的例子(至少我已经能够找到)在与外部API接口时使用POST / PUT而不是GET。任何帮助或指导都会受到赞赏 - 即使只是因为我正在以错误的方式思考这个问题。 :d
答案 0 :(得分:3)
GET/PUT/POST
方面与问题无关。总的来说,您的问题实际上归结为the frequently asked question "How do I split logic between action creators and reducers?"。引用那个答案:
对于减速器或动作创建者应该采用哪些逻辑,没有一个明确的答案。一些开发人员更喜欢拥有“胖”动作创建器,其中“瘦”缩减器只是简单地将动作中的数据接收并盲目地将其合并到相应的状态中。其他人试图强调保持尽可能小的动作,并最小化动作创建者中getState()的使用。 (出于这个问题的目的,其他异步方法,如sagas和observables属于“行动创造者”类别。)
将更多逻辑放入减速机有一些潜在的好处。动作类型可能更具语义性和更有意义(例如“USER_UPDATED”而不是“SET_STATE”)。此外,在reducer中使用更多逻辑意味着更多功能将受到时间旅行调试的影响。
这个评论很好地总结了二分法:
现在,问题是在动作创建器中放置什么,以及在reducer中,fat和thin动作对象之间的选择。如果将所有逻辑放在动作创建器中,最终会生成基本上声明状态更新的胖动作对象。 Reducers变得纯粹,愚蠢,添加 - 删除它,更新这些功能。它们很容易构成。但是你的业务逻辑并不多。如果你在reducer中加入了更多的逻辑,你最终会得到漂亮的瘦动作对象,大部分数据逻辑都集中在一个地方,但是你的reducer更难以编写,因为你可能需要来自其他分支的信息。你最终会得到大型减速器或减速器,这些减速器或减速器会从该州的较高位置获得额外的参数。
我前段时间也写了my own thoughts on "thick and thin reducers"。
所以,最终这取决于你喜欢如何构建逻辑。