假设我有以下内容:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
}
}
在那个动作创建器中,我想访问全局存储状态(所有还原器)。这样做更好吗:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
或:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
对于在动作创建者中访问状态是否是一个好主意,有不同的意见:
Redux creator Dan Abramov feels that it should be limited: "The few use cases where I think it’s acceptable is for checking cached data before you make a request, or for checking whether you are authenticated (in other words, doing a conditional dispatch). I think that passing data such as state.something.items in an action creator is definitely an anti-pattern and is discouraged because it obscured the change history: if there is a bug and items are incorrect, it is hard to trace where those incorrect values come from because they are already part of the action, rather than directly computed by a reducer in response to an action. So do this with care."
Current Redux maintainer Mark Erikson says it's fine and even encouraged to use getState in thunks - that's why it exists. He discusses the pros and cons of accessing state in action creators in his blog post Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability.
如果您发现您需要这样做,那么您建议的两种方法都可以。第一种方法不需要任何中间件:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
然而,你可以看到它依赖于从某个模块导出的单例存储。我们不建议这样做,因为这样会使在应用程序中添加服务器渲染变得更加困难,因为在大多数情况下,在服务器上,每个请求都需要一个单独的存储。因此,虽然从技术上讲这种方法是可行的,但我们不建议从模块中导出存储。
这就是为什么我们推荐第二种方法:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
这需要你使用Redux坦克中间件,但它在客户端和服务器上都工作得很好。你可以阅读更多关于Redux坦克和为什么它在这种情况下是必要的在这里。
理想情况下,您的操作不应该“臃肿”,并且应该包含尽可能少的信息,但是您应该在自己的应用程序中自由地执行最适合自己的操作。Redux常见问题解答中有关于拆分操作创建者和还原器之间逻辑的信息,以及在操作创建者中使用getState可能有用的时间。
我想指出的是,从存储中读取数据并没有那么糟糕——根据存储决定应该做什么,可能比将所有内容都传递给组件然后作为函数的参数要方便得多。我完全同意Dan的观点,最好不要将store单独使用,除非你100%确定只用于客户端渲染(否则很难跟踪可能出现的bug)。
我最近创建了一个库来处理redux的冗长,我认为把所有东西都放在中间件中是一个好主意,这样你就可以把所有东西都作为依赖注入。
你的例子是这样的:
import { createSyncTile } from 'redux-tiles';
const someTile = createSyncTile({
type: ['some', 'tile'],
fn: ({ params, selectors, getState }) => {
return {
data: params.data,
items: selectors.another.tile(getState())
};
},
});
然而,正如您所看到的,我们在这里并没有真正修改数据,所以很有可能我们可以在其他地方使用这个选择器来组合其他地方的数据。
我知道我来这里有点晚了,但我来这里是为了表达我对在行动中使用状态的渴望,然后形成了我自己的想法,当我意识到什么是我认为正确的行为时。
这就是选择器对我最有意义的地方。发出此请求的组件应该被告知是否该通过选择发出该请求。
export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
return (dispatch) => {
dispatch(anotherAction(items));
}
}
这可能感觉像是泄露了抽象,但是您的组件显然需要发送消息,并且消息有效负载应该包含相关的状态。不幸的是,你的问题没有一个具体的例子,因为我们可以通过一个“更好的模型”的选择器和动作。
当您的场景很简单时,您可以使用
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
但有时你的动作创建者需要触发多个动作
例如异步请求,所以你需要
请求加载请求加载成功请求加载失败动作
export const [REQUEST_LOAD, REQUEST_LOAD_SUCCESS, REQUEST_LOAD_FAIL] = [`REQUEST_LOAD`
`REQUEST_LOAD_SUCCESS`
`REQUEST_LOAD_FAIL`
]
export function someAction() {
return (dispatch, getState) => {
const {
items
} = getState().otherReducer;
dispatch({
type: REQUEST_LOAD,
loading: true
});
$.ajax('url', {
success: (data) => {
dispatch({
type: REQUEST_LOAD_SUCCESS,
loading: false,
data: data
});
},
error: (error) => {
dispatch({
type: REQUEST_LOAD_FAIL,
loading: false,
error: error
});
}
})
}
}
注意:你需要redux-thunk在动作创建器中返回函数