【问题标题】:Best practice for updating individual state properties with Redux Saga使用 Redux Saga 更新单个状态属性的最佳实践
【发布时间】:2017-11-07 04:05:53
【问题描述】:

所以我正在使用 Redux Saga 在 React 中实现一个应用程序,我对我的特定用例的信息如此之少感到有点困惑,因为它看起来并不奇怪。很可能我使用了错误的术语或以错误的方式思考问题,因为我对 React/Redux 相当陌生。无论如何,我在谷歌上搜索这个问题的所有尝试都阻碍了我,我希望能从比我更有经验的人那里获得一些见解。

我的应用程序状态上有一个userSettings 属性,它管理登录用户的一些配置选项。在应用程序的某个时刻,用户可以翻转开关以禁用“一目了然”仪表板小部件的显示,我需要将此信息传递给后端 API 以更新他们在数据库中的设置信息,然后根据本次后端更新是否成功更新状态。

我的代码目前有一个用于所有用户设置更新的主要 saga,我打算通过一个更具体的 saga 来实现这个设置,因此:

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());
    }
}

如代码中所述,我的问题是我不确定将更新值合并到状态的逻辑应该在哪里/如何发生。据我所知,我有三个选择:

  1. 在分发初始动作之前在组件中构建更新状态,即:

    hasDashboardAtAGlanceHiddenToggle() {
        const { dispatch, userSettings } = this.props;
        const newState = Object.assign({}, userSettings , {
            hasDashboardAtAGlanceHidden: !userSettings.hasDashboardAtAGlanceHidden
        });
        dispatch(setHasDashboardAtAGlanceHidden(userSettings));
    }
    

    }

  2. 使用redux-saga的select效果,在更具体的初始saga中构建完整的状态对象,即:

    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));
    }
    
  3. 在更新之前检索服务器的用户设置对象副本,即:

    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

【问题讨论】:

    标签: javascript reactjs redux react-redux redux-saga


    【解决方案1】:

    GET/PUT/POST 方面与问题无关。总的来说,你的问题真的归结为the frequently asked question "How do I split logic between action creators and reducers?"。引用那个答案:

    对于 reducer 或 action creator 中应该包含哪些逻辑,没有一个明确的答案。一些开发人员更喜欢拥有“胖”的动作创建者,而“瘦”的减速器只是简单地将数据放入动作中并盲目地将其合并到相应的状态中。其他人则试图强调让动作尽可能小,并尽量减少在动作创建者中使用 getState()。 (对于这个问题,其他异步方法,如 sagas 和 observables 属于“动作创建者”类别。)

    在你的 reducer 中加入更多的逻辑有一些潜在的好处。操作类型可能更符合语义和更有意义(例如“USER_UPDATED”而不是“SET_STATE”)。此外,在 reducer 中拥有更多逻辑意味着更多功能会受到时间旅行调试的影响。

    这条评论很好地总结了二分法:

    现在,问题是在 action creator 中放入什么,在 reducer 中放入什么,在胖和瘦动作对象之间进行选择。如果你把所有的逻辑都放在动作创建器中,你最终会得到基本上声明状态更新的胖动作对象。 Reducers 变得纯粹、愚蠢、添加这个、删除那个、更新这些功能。他们将很容易组成。但是你的业务逻辑并不多。如果你在 reducer 中加入更多逻辑,你最终会得到漂亮、精简的 action 对象,大部分数据逻辑都在一个地方,但是你的 reducer 更难组合,因为你可能需要来自其他分支的信息。您最终会得到大型化简器或从该州更高层获取额外参数的化简器。

    不久前我还写了my own thoughts on "thick and thin reducers"。

    所以,归根结底,这取决于您喜欢如何构建逻辑。

    【讨论】:

    • 太棒了,非常感谢。 :) 这很有帮助。我想,我对有一个确切的“正确方法”来做这件事太感兴趣了。
    猜你喜欢
    • 2019-01-01
    • 2011-06-30
    • 2021-02-20
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    相关资源
    最近更新 更多