javascript - 使用 Redux Saga 更新单个状态属性的最佳实践

标签 javascript reactjs redux react-redux redux-saga

所以我正在使用 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. 在分派(dispatch)初始操作之前在组件中构建更新状态,即:

    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

最佳答案

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

There's no single clear answer to exactly what pieces of logic should go in a reducer or an action creator. Some developers prefer to have “fat” action creators, with “thin” reducers that simply take the data in an action and blindly merge it into the corresponding state. Others try to emphasize keeping actions as small as possible, and minimize the usage of getState() in an action creator. (For purposes of this question, other async approaches such as sagas and observables fall in the "action creator" category.)

There are some potential benefits from putting more logic into your reducers. It's likely that the action types would be more semantic and more meaningful (such as "USER_UPDATED" instead of "SET_STATE"). In addition, having more logic in reducers means that more functionality will be affected by time travel debugging.

This comment sums up the dichotomy nicely:

Now, the problem is what to put in the action creator and what in the reducer, the choice between fat and thin action objects. If you put all the logic in the action creator, you end up with fat action objects that basically declare the updates to the state. Reducers become pure, dumb, add-this, remove that, update these functions. They will be easy to compose. But not much of your business logic will be there. If you put more logic in the reducer, you end up with nice, thin action objects, most of your data logic in one place, but your reducers are harder to compose since you might need info from other branches. You end up with large reducers or reducers that take additional arguments from higher up in the state.

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

因此,归根结底,这取决于您更喜欢如何构建逻辑。

关于javascript - 使用 Redux Saga 更新单个状态属性的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47149807/

相关文章:

具有不同图像的 Javascript 计时器

javascript - 无法访问@font-face 中提到的字体文件

javascript - 将 React Ref 从父级传递给子级以获得 DOM 元素

javascript - 将路由器参数传递给深层嵌套组件

javascript - React Native - JS 文件中的 TypeScript?

Javascript - Node.js - 防止函数的多次执行

reactjs - Material-UI @next 不支持基于 props 的样式?

javascript - 在 React 中,当屏幕尺寸 < 875px 时,如何删除(仅)父元素?

javascript - Redux 应用程序中的 mapStateToProps() 嵌套状态?

javascript - 在所有主题名称字段上显示错误