Javascript 在动作创建者中访问 Redux 状态?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35667249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Accessing Redux state in an action creator?
提问by ffxsam
Say I have the following:
说我有以下几点:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
}
}
And in that action creator, I want to access the global store state (all reducers). Is it better to do this:
在那个动作创建者中,我想访问全局存储状态(所有减速器)。这样做是否更好:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
or this:
或这个:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
回答by Dan Abramov
There are differing opinions on whether accessing state in action creators is a good idea:
关于在 action creators 中访问 state 是否是一个好主意,有不同的意见:
- 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 datasuch 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 anditems
are incorrect, it is hard to trace wherethose 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.
- Redux 创建者 Dan Abramov 认为它应该受到限制:“我认为可以接受的少数用例是在您发出请求之前检查缓存数据,或者检查您是否已通过身份验证(换句话说,进行条件分派)。我认为,通过数据如
state.something.items
在行动的创建者绝对是一个反模式和气馁,因为它掩盖了改变历史:如果有一个错误,items
是不正确的,这是很难追查,其中那些不正确的值来自于,因为它们是已经是动作的一部分,而不是由减速器直接计算以响应动作。所以要小心。” - 当前 Redux 维护者 Mark Erikson 说它很好,甚至鼓励
getState
在 thunk 中使用- 这就是它存在的原因。他在他的博客文章Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability 中讨论了在 action creators 中访问 state 的利弊。
If you find that you need this, both approaches you suggested are fine. The first approach does not require any middleware:
如果你发现你需要这个,你建议的两种方法都很好。第一种方法不需要任何中间件:
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
However you can see that it relies on store
being a singleton exported from some module. We don't recommend thatbecause it makes it much harder to add server rendering to your appbecause in most cases on the server you'll want to have a separate store per request. So while technically this approach works, we don't recommend exporting a store from a module.
但是你可以看到它依赖于store
从某个模块导出的单例。我们不建议这样做,因为它会使向您的应用程序添加服务器渲染变得更加困难,因为在大多数情况下,您希望在服务器上为每个请求创建一个单独的存储。因此,虽然从技术上讲这种方法有效,但我们不建议从模块导出商店。
This is why we recommend the second approach:
这就是我们推荐第二种方法的原因:
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
It would require you to use Redux Thunk middleware but it works fine both on the client and on the server. You can read more about Redux Thunk and whyit's necessary in this case here.
它需要您使用 Redux Thunk 中间件,但它在客户端和服务器上都可以正常工作。您可以在此处阅读有关 Redux Thunk 的更多信息,以及为什么在这种情况下它是必要的。
Ideally, your actions should not be “fat” and should contain as little information as possible, but you should feel free to do what works best for you in your own application. The Redux FAQ has information on splitting logic between action creators and reducersand times when it may be useful to use getState
in an action creator.
理想情况下,你的动作不应该是“胖”的,应该包含尽可能少的信息,但你应该在自己的应用程序中自由地做最适合你的事情。的终极版FAQ对信息行动的创作者和减速器之间拆分逻辑和时间时,它可以是使用有用的getState
一个动作的创造者。
回答by Nour Sammour
When your scenario is simple you can use
当您的场景很简单时,您可以使用
import store from '../store';
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
But sometimes your action creator
need to trigger multi actions
但有时你action creator
需要触发多个动作
for example async request so you need
REQUEST_LOAD
REQUEST_LOAD_SUCCESS
REQUEST_LOAD_FAIL
actions
例如异步请求,所以你需要
REQUEST_LOAD
REQUEST_LOAD_SUCCESS
REQUEST_LOAD_FAIL
采取行动
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
});
}
})
}
}
Note:you need redux-thunkto return function in action creator
注意:你需要redux-thunk在 action creator 中返回函数
回答by Jason Allshorn
I agree with @Bloomca. Passing the value needed from the store into the dispatch function as an argument seems simpler than exporting the store. I made an example here:
我同意@Bloomca。将 store 所需的值作为参数传递给 dispatch 函数似乎比导出 store 更简单。我在这里做了一个例子:
import React from "react";
import {connect} from "react-redux";
import * as actions from '../actions';
class App extends React.Component {
handleClick(){
const data = this.props.someStateObject.data;
this.props.someDispatchFunction(data);
}
render(){
return (
<div>
<div onClick={ this.handleClick.bind(this)}>Click Me!</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return { someStateObject: state.someStateObject };
};
const mapDispatchToProps = (dispatch) => {
return {
someDispatchFunction:(data) => { dispatch(actions.someDispatchFunction(data))},
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
回答by Bloomca
I would like to point out that it is not that bad to read from the store -- it might be just much more convenient to decide what should be done based on the store, than to pass everything to the component and then as a parameter of a function. I agree with Dan completely, that it is much better not to use store as a singletone, unless you are 100% sure that you will use only for client-side rendering (otherwise hard to trace bugs might appear).
我想指出,从 store 中读取并没有那么糟糕——根据 store 决定应该做什么可能比将所有内容传递给组件然后作为参数更方便一个函数。我完全同意 Dan 的观点,最好不要将 store 用作单调,除非您 100% 确定将仅用于客户端渲染(否则可能会出现难以追踪的错误)。
I have created a libraryrecently to deal with verbosity of redux, and I think it is a good idea to put everything in the middleware, so you have everyhing as a dependency injection.
我最近创建了一个库来处理 redux 的冗长,我认为把所有东西都放在中间件中是个好主意,这样你就可以将所有东西都作为依赖注入。
So, your example will look like that:
因此,您的示例将如下所示:
import { createSyncTile } from 'redux-tiles';
const someTile = createSyncTile({
type: ['some', 'tile'],
fn: ({ params, selectors, getState }) => {
return {
data: params.data,
items: selectors.another.tile(getState())
};
},
});
However, as you can see, we don't really modify data here, so there is a good chance that we can just use this selector in other place to combine it somewhere else.
然而,正如你所看到的,我们并没有真正修改这里的数据,所以很有可能我们可以在其他地方使用这个选择器将它组合到其他地方。
回答by Andrei Cioara
Presenting an alternative way of solving this. This may be better or worse than Dan's solution, depending on your application.
提出解决此问题的替代方法。这可能比 Dan 的解决方案更好或更差,具体取决于您的应用。
You can get the state from the reducers into the actions by splitting the action in 2 separate functions: first ask for the data, second act on the data. You can do that by using redux-loop
.
您可以通过将操作拆分为 2 个单独的函数来将来自 reducer 的状态获取到操作中:首先请求数据,然后对数据进行操作。您可以使用redux-loop
.
First 'kindly ask for the data'
首先“请提供数据”
export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
}
}
In the reducer, intercept the ask and provide the data to the second stage action by using redux-loop
.
在reducer中,拦截ask并使用 提供数据给第二阶段的action redux-loop
。
import { loop, Cmd } from 'redux-loop';
const initialState = { data: '' }
export default (state=initialState, action) => {
switch(action.type) {
case SOME_ACTION: {
return loop(state, Cmd.action(anotherAction(state.data))
}
}
}
With the data in hand, do whatever you initially wanted
有了手头的数据,做任何你最初想做的事
export const ANOTHER_ACTION = 'ANOTHER_ACTION';
export function anotherAction(data) {
return {
type: ANOTHER_ACTION,
payload: data,
}
}
Hope this helps someone.
希望这可以帮助某人。
回答by SqueeDee
I know I'm late to the party here, but I came here for opinions on my own desire to use state in actions, and then formed my own, when I realized what I think is the correct behavior.
我知道我在这里参加聚会迟到了,但是我来到这里是为了对自己在行动中使用状态的愿望提出意见,然后形成自己的意见,当我意识到我认为什么是正确的行为时。
This is where a selector makes the most sense to me. Your component that issues this request should be told wether it's time to issue it through selection.
这是选择器对我来说最有意义的地方。发出此请求的组件应该被告知是时候通过选择发出它了。
export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
return (dispatch) => {
dispatch(anotherAction(items));
}
}
It might feel like leaking abstractions, but your component clearly needs to send a message and the message payload should contain pertinent state. Unfortunately your question doesn't have a concrete example because we could work through a 'better model' of selectors and actions that way.
可能感觉像是在泄漏抽象,但您的组件显然需要发送消息,并且消息有效负载应包含相关状态。不幸的是,您的问题没有具体的例子,因为我们可以通过这种方式处理选择器和操作的“更好模型”。
回答by Sam96
I would like to suggest yet another alternative that I find the cleanest, but it requires react-redux
or something simular - also I'm using a few other fancy features along the way:
我想建议另一种我认为最干净的替代方案,但它需要react-redux
或类似的东西-我还在此过程中使用了其他一些奇特的功能:
// actions.js
export const someAction = (items) => ({
type: 'SOME_ACTION',
payload: {items},
});
// Component.jsx
import {connect} from "react-redux";
const Component = ({boundSomeAction}) => (<div
onClick={boundSomeAction}
/>);
const mapState = ({otherReducer: {items}}) => ({
items,
});
const mapDispatch = (dispatch) => bindActionCreators({
someAction,
}, dispatch);
const mergeProps = (mappedState, mappedDispatches) => {
// you can only use what gets returned here, so you dont have access to `items` and
// `someAction` anymore
return {
boundSomeAction: () => mappedDispatches.someAction(mappedState.items),
}
});
export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);
// (with other mapped state or dispatches) Component.jsx
import {connect} from "react-redux";
const Component = ({boundSomeAction, otherAction, otherMappedState}) => (<div
onClick={boundSomeAction}
onSomeOtherEvent={otherAction}
>
{JSON.stringify(otherMappedState)}
</div>);
const mapState = ({otherReducer: {items}, otherMappedState}) => ({
items,
otherMappedState,
});
const mapDispatch = (dispatch) => bindActionCreators({
someAction,
otherAction,
}, dispatch);
const mergeProps = (mappedState, mappedDispatches) => {
const {items, ...remainingMappedState} = mappedState;
const {someAction, ...remainingMappedDispatch} = mappedDispatch;
// you can only use what gets returned here, so you dont have access to `items` and
// `someAction` anymore
return {
boundSomeAction: () => someAction(items),
...remainingMappedState,
...remainingMappedDispatch,
}
});
export const ConnectedComponent = connect(mapState, mapDispatch, mergeProps)(Component);
If you want to reuse this you'll have to extract the specific mapState
, mapDispatch
and mergeProps
into functions to reuse elsewhere, but this makes dependencies perfectly clear.
如果你想重用它,你必须提取特定的mapState
,mapDispatch
并mergeProps
放入函数中以在其他地方重用,但这使得依赖关系非常清晰。