在 React-Redux 中转换 API 数据:Action 或 Reducer
Converting API data in React-Redux: Action or Reducer
我有一个天气应用程序,api 数据有不同的格式,所以我创建了一个方法对象,我可以将该数据转换为英制格式,并将时间从 UTC 转换为 GMT。
现在我在 reducer 中的数据上调用了这些方法。
问题:是 "okay" 还是应该在将有效负载传送到 reducer 之前在相应的操作中完成转换?只是好奇这样的最佳实践是什么。
仅供参考:我使用 axios
作为基于 promise 的 HTTP 客户端,并使用 redux-promise-middleware
、redux-lodger
和 redux-promise
作为商店中的中间件。
动作创作者:
export const fetchCurrentWeather = (city) => {
const url = `${CURRENT_ROOT_URL}&q=${city},us`;
const promise = new Promise((resolve, reject) => {
axios.get(url)
.then(res => resolve(res.data))
.catch(err => reject(err));
});
return {
type: FETCH_CURRENT_WEATHER,
payload: promise
};
};
减速器:
export default(state = initialState, action) => {
const data = action.payload;
switch (action.type) {
case `${FETCH_CURRENT_WEATHER}_PENDING`:
return {};
case `${FETCH_CURRENT_WEATHER}_FULFILLED`:
const prefix = 'wi wi-owm-';
const code = data.weather[0].id;
const icon = prefix + code;
return {
...state,
weatherData: {
humidity: data.main.humidity,
icon,
name: data.name,
pressure: unitConverter.toInchesHG(data.main.pressure),
sunrise: unitConverter.toGMT(data.sys.sunrise),
sunset: unitConverter.toGMT(data.sys.sunset),
temp: unitConverter.toFarenheit(data.main.temp),
winddir: unitConverter.toCardinal(data.wind.deg),
windspd: unitConverter.toMPH(data.wind.speed)
},
isFetched: true
};
case `${FETCH_CURRENT_WEATHER}_REJECTED`:
return {
...state,
isFetched: true,
err: data
};
default:
return state;
}
};
您可以在三个位置有效地处理您的原始数据:
组件的render()
函数
这通常不是一个好主意,因为这意味着每次渲染组件时都会处理数据。如果你使用像 reselect 这样的包,你可以通过缓存来缓解性能问题,但即便如此,实际的代码例如sorting 或 filtering 应保留在 mapStateToProps()
.
中减速机
在 reducer 中处理数据可以有一个更好的案例,但我认为出于清晰和关注点分离的原因,这仍然不是最好的地方。 reducer 的工作非常明确 - 承认动作并将先前状态与动作结果合并,任何其他事情只会模糊责任和可测试性的界限。
一个动作 thunk
在我看来,action thunk 是一次性数据转换(例如导入原始数据的规范化/转换)的正确位置。它不仅通常是一个明确的操作子任务(例如获取天气数据 -> 将摄氏度转换为华氏度),而且还具有不存储无用数据的额外优势,即使是暂时的状态。
引用 Dan Abramov 的话:
... action objects [are] minimal representations of what happened and
state objects [are] minimal representations of what’s necessary for
rendering right now.
最后的说明 - 选择器
虽然我上面说组件不是执行原始数据转换的好地方,但实际上我认为存储 原始数据 在 redux-state 中并使用像 reselect 这样的包来根据需要通过 selectors
呈现规范化或计算的值。
实现此目的的一种方法是使用 selector
函数对原始数据的定义部分执行数据规范化。使用 reselect 包,此转换将被缓存,因此只执行一次。它的优点是 延迟转换 仅在需要时才转换数据。
我有一个天气应用程序,api 数据有不同的格式,所以我创建了一个方法对象,我可以将该数据转换为英制格式,并将时间从 UTC 转换为 GMT。
现在我在 reducer 中的数据上调用了这些方法。
问题:是 "okay" 还是应该在将有效负载传送到 reducer 之前在相应的操作中完成转换?只是好奇这样的最佳实践是什么。
仅供参考:我使用 axios
作为基于 promise 的 HTTP 客户端,并使用 redux-promise-middleware
、redux-lodger
和 redux-promise
作为商店中的中间件。
动作创作者:
export const fetchCurrentWeather = (city) => {
const url = `${CURRENT_ROOT_URL}&q=${city},us`;
const promise = new Promise((resolve, reject) => {
axios.get(url)
.then(res => resolve(res.data))
.catch(err => reject(err));
});
return {
type: FETCH_CURRENT_WEATHER,
payload: promise
};
};
减速器:
export default(state = initialState, action) => {
const data = action.payload;
switch (action.type) {
case `${FETCH_CURRENT_WEATHER}_PENDING`:
return {};
case `${FETCH_CURRENT_WEATHER}_FULFILLED`:
const prefix = 'wi wi-owm-';
const code = data.weather[0].id;
const icon = prefix + code;
return {
...state,
weatherData: {
humidity: data.main.humidity,
icon,
name: data.name,
pressure: unitConverter.toInchesHG(data.main.pressure),
sunrise: unitConverter.toGMT(data.sys.sunrise),
sunset: unitConverter.toGMT(data.sys.sunset),
temp: unitConverter.toFarenheit(data.main.temp),
winddir: unitConverter.toCardinal(data.wind.deg),
windspd: unitConverter.toMPH(data.wind.speed)
},
isFetched: true
};
case `${FETCH_CURRENT_WEATHER}_REJECTED`:
return {
...state,
isFetched: true,
err: data
};
default:
return state;
}
};
您可以在三个位置有效地处理您的原始数据:
组件的
render()
函数这通常不是一个好主意,因为这意味着每次渲染组件时都会处理数据。如果你使用像 reselect 这样的包,你可以通过缓存来缓解性能问题,但即便如此,实际的代码例如sorting 或 filtering 应保留在
mapStateToProps()
.中减速机
在 reducer 中处理数据可以有一个更好的案例,但我认为出于清晰和关注点分离的原因,这仍然不是最好的地方。 reducer 的工作非常明确 - 承认动作并将先前状态与动作结果合并,任何其他事情只会模糊责任和可测试性的界限。
一个动作 thunk
在我看来,action thunk 是一次性数据转换(例如导入原始数据的规范化/转换)的正确位置。它不仅通常是一个明确的操作子任务(例如获取天气数据 -> 将摄氏度转换为华氏度),而且还具有不存储无用数据的额外优势,即使是暂时的状态。
引用 Dan Abramov 的话:
... action objects [are] minimal representations of what happened and state objects [are] minimal representations of what’s necessary for rendering right now.
最后的说明 - 选择器
虽然我上面说组件不是执行原始数据转换的好地方,但实际上我认为存储 原始数据 在 redux-state 中并使用像 reselect 这样的包来根据需要通过 selectors
呈现规范化或计算的值。
实现此目的的一种方法是使用 selector
函数对原始数据的定义部分执行数据规范化。使用 reselect 包,此转换将被缓存,因此只执行一次。它的优点是 延迟转换 仅在需要时才转换数据。