在 redux 中修改响应数据的合理位置在哪里?
Where is the sensible place to modify response data in redux?
结合使用 React、Redux 和 Thunk,我有以下内容:
actions.js
import $ from 'jquery';
import * as types from '../constants/ActionTypes';
import { API_PATH } from '../constants/Config';
export function coursesLoaded(courses) {
return { type: types.COURSES_LOADED, courses };
}
export function fetchData() {
return (dispatch) => {
return $.getJSON(API_PATH).then((response) => {
dispatch(coursesLoaded(response.result));
});
};
}
reducer.js
import { routerReducer as routing } from 'react-router-redux';
import { combineReducers } from 'redux';
import * as types from '../constants/ActionTypes';
const initialState = {
courses: [],
};
function main(state = initialState, action) {
switch(action.type) {
case types.COURSES_LOADED:
return {
...state,
courses: action.courses,
};
default:
return state;
}
}
const rootReducer = combineReducers({ main, routing });
export default rootReducer;
上面的两个片段很好,我觉得它们符合 Redux 的意图。我现在想在响应中返回的字段到达容器之前对其进行一些修改。
例如,响应可能是:
[
{ code: "R101", name: "Intro to Redux", author: "Dan" },
{ code: "R102", name: "Middleware", author: "Dan" },
]
我想将其更改为(简单的简单示例):
[
{ code: "R101", name: "Intro to Redux", author: "Dan", additionalProperty: "r101_intro_to_redux" },
{ code: "R102", name: "Middleware", author: "Dan", additionalProperty: "r102_middleware" },
]
目前研究
选项一
查看 Redux 上的异步示例,我可以看到这里的响应有轻微的变化:
https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L33
选项二
查看其他 Whosebug 问题,这让我相信将其排除在操作之外更有意义,因为 reducers 应该是修改状态的东西(但也许这并不真正算作状态?):
Redux - where to prepare data
选项三
我倾向于认为这是中间件的工作——这就是 normalizr 处理它的方式,但我找不到任何非被动中间件示例。如果中间件是这里的目标,中间件应该调度某种 SET_STATE 操作,还是可以在中间件中自由更新状态?
编辑
试验了一些中间件,例如:
import { lowerCase, snakeCase } from 'lodash';
import * as types from '../constants/ActionTypes';
export default store => next => action => {
if(action.type == types.COURSES_LOADED) {
action.courses = action.courses.map((course) => {
course.additionalProperty = snakeCase(lowerCase(`${course.code} ${course.name}`));
return course;
});
}
return next(action);
}
看起来还不错——这真的是中间件的用意吗?原始问题成立 - 理想地点在哪里?
对于我来说,我在行动中做这种事情(coursesLoaded
或fetchData
)。
原因如下:
- 这不是存储 material,这只是外部数据管理,因此与应该更改存储状态的 reducer 无关
- 不同的 reducer 实际上可能需要相同的更正数据,例如,假设您有另一个 reducer 收集所有
additionalProperty
用于实现目的,因此在操作中这样做可确保将正确的数据发送到所有 reducer .
- 这不是中间件的典型工作,它只特定于一个动作,而如果中间件以相同的方式被一堆动作使用,它就会很有用。此外,使用中间件更加晦涩并将它与 reader 分开。拥有 action->reducer 就简单多了,也没有什么大的缺点。
或者您可以使用 selector,并在 redux 存储中保留原始文件。
const mapStateToProps = state => {
courses: mySelectorThatAddsFields(state.courses),
}
结合使用 React、Redux 和 Thunk,我有以下内容:
actions.js
import $ from 'jquery';
import * as types from '../constants/ActionTypes';
import { API_PATH } from '../constants/Config';
export function coursesLoaded(courses) {
return { type: types.COURSES_LOADED, courses };
}
export function fetchData() {
return (dispatch) => {
return $.getJSON(API_PATH).then((response) => {
dispatch(coursesLoaded(response.result));
});
};
}
reducer.js
import { routerReducer as routing } from 'react-router-redux';
import { combineReducers } from 'redux';
import * as types from '../constants/ActionTypes';
const initialState = {
courses: [],
};
function main(state = initialState, action) {
switch(action.type) {
case types.COURSES_LOADED:
return {
...state,
courses: action.courses,
};
default:
return state;
}
}
const rootReducer = combineReducers({ main, routing });
export default rootReducer;
上面的两个片段很好,我觉得它们符合 Redux 的意图。我现在想在响应中返回的字段到达容器之前对其进行一些修改。
例如,响应可能是:
[
{ code: "R101", name: "Intro to Redux", author: "Dan" },
{ code: "R102", name: "Middleware", author: "Dan" },
]
我想将其更改为(简单的简单示例):
[
{ code: "R101", name: "Intro to Redux", author: "Dan", additionalProperty: "r101_intro_to_redux" },
{ code: "R102", name: "Middleware", author: "Dan", additionalProperty: "r102_middleware" },
]
目前研究
选项一 查看 Redux 上的异步示例,我可以看到这里的响应有轻微的变化: https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L33
选项二 查看其他 Whosebug 问题,这让我相信将其排除在操作之外更有意义,因为 reducers 应该是修改状态的东西(但也许这并不真正算作状态?): Redux - where to prepare data
选项三 我倾向于认为这是中间件的工作——这就是 normalizr 处理它的方式,但我找不到任何非被动中间件示例。如果中间件是这里的目标,中间件应该调度某种 SET_STATE 操作,还是可以在中间件中自由更新状态?
编辑
试验了一些中间件,例如:
import { lowerCase, snakeCase } from 'lodash';
import * as types from '../constants/ActionTypes';
export default store => next => action => {
if(action.type == types.COURSES_LOADED) {
action.courses = action.courses.map((course) => {
course.additionalProperty = snakeCase(lowerCase(`${course.code} ${course.name}`));
return course;
});
}
return next(action);
}
看起来还不错——这真的是中间件的用意吗?原始问题成立 - 理想地点在哪里?
对于我来说,我在行动中做这种事情(coursesLoaded
或fetchData
)。
原因如下:
- 这不是存储 material,这只是外部数据管理,因此与应该更改存储状态的 reducer 无关
- 不同的 reducer 实际上可能需要相同的更正数据,例如,假设您有另一个 reducer 收集所有
additionalProperty
用于实现目的,因此在操作中这样做可确保将正确的数据发送到所有 reducer . - 这不是中间件的典型工作,它只特定于一个动作,而如果中间件以相同的方式被一堆动作使用,它就会很有用。此外,使用中间件更加晦涩并将它与 reader 分开。拥有 action->reducer 就简单多了,也没有什么大的缺点。
或者您可以使用 selector,并在 redux 存储中保留原始文件。
const mapStateToProps = state => {
courses: mySelectorThatAddsFields(state.courses),
}