在动作创建者中访问Redux状态?

假设我有以下内容:

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));
}
}
211445 次浏览

当您的场景很简单时,您可以使用

import store from '../store';


export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}

但是有时候你的action creator需要触发多个动作

为例异步请求,所以你需要 REQUEST_LOAD REQUEST_LOAD_SUCCESS REQUEST_LOAD_FAIL actions

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在动作创建器中返回函数

对于在动作创建者中访问状态是否是一个好主意,有不同的意见:

  • Redux的创造者Dan Abramov认为它应该受到限制:“我认为它可以接受的少数用例是在你发出请求之前检查缓存数据,或者检查你是否被验证(换句话说,做一个有条件的分派)。我认为在动作创建者中传递数据(例如state.something.items)绝对是一种反模式,不建议这样做,因为它掩盖了更改历史:如果有一个错误并且items是不正确的,那么很难跟踪在哪里这些不正确的值来自哪里,因为它们已经是动作的一部分,而不是由reducer响应动作时直接计算出来的。所以要小心行事。”
  • 当前Redux维护者Mark Erikson说它很好,甚至鼓励在thks中使用getState -这就是它存在的原因。他在他的博客文章成语还原:关于坦克,传说,抽象和可重用性的思考中讨论了在动作创建者中访问状态的利与弊。

如果您发现您需要这样做,那么您建议的两种方法都可以。第一种方法不需要任何中间件:

import store from '../store';


export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}

然而,你可以看到它依赖于store是一个从某个模块导出的单例。我们不建议这样做,因为它使得添加服务器渲染到您的应用程序更加困难,因为在大多数情况下在服务器上,每个请求都有一个单独的存储。因此,虽然从技术上讲这种方法是可行的,但我们不建议从模块中导出存储。

这就是为什么我们推荐第二种方法:

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())
};
},
});

然而,正如您所看到的,我们在这里并没有真正修改数据,所以很有可能我们可以在其他地方使用这个选择器来组合其他地方的数据。

我同意@Bloomca。将存储所需的值作为参数传递给分派函数似乎比导出存储简单。我举个例子:

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);

提出解决这个问题的另一种方法。这可能比Dan的解决方案更好,也可能更差,这取决于您的应用程序。

您可以通过将操作拆分为2个单独的函数来将状态从约简到操作中:第一个请求数据,第二个操作数据。你可以通过使用redux-loop来做到这一点。

首先“请提供数据”

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
return {
type: SOME_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))
}
}
}

有了数据,就可以做最初想做的事情了

export const ANOTHER_ACTION = 'ANOTHER_ACTION';
export function anotherAction(data) {
return {
type: ANOTHER_ACTION,
payload: data,
}
}

希望这能帮助到一些人。

我知道我来这里有点晚了,但我来这里是为了表达我对在行动中使用状态的渴望,然后形成了我自己的想法,当我意识到什么是我认为正确的行为时。

这就是选择器对我最有意义的地方。发出此请求的组件应该被告知是否该通过选择发出该请求。

export const SOME_ACTION = 'SOME_ACTION';
export function someAction(items) {
return (dispatch) => {
dispatch(anotherAction(items));
}
}

这可能感觉像是泄露了抽象,但是您的组件显然需要发送消息,并且消息有效负载应该包含相关的状态。不幸的是,你的问题没有一个具体的例子,因为我们可以通过一个“更好的模型”的选择器和动作。

我想建议另一个我认为最干净的替代方案,但它需要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);

如果你想重用它,你必须将特定的mapStatemapDispatchmergeProps提取到函数中以在其他地方重用,但这使得依赖关系非常清楚。

我不会在动作创建者中访问状态。我将使用mapStateToProps()并导入整个状态对象,并在动作创建者最终要去的组件中导入一个组合reducer文件(或import * from './reducers';)。然后在组件中使用解构来使用状态道具中需要的任何东西。如果Action Creator将状态传递给给定TYPE的Reducer,则不需要提及state,因为Reducer可以访问当前设置在state中的所有内容。您的示例没有更新任何内容。我只使用Action Creator从它的参数传递状态。

在减速器中执行如下操作:

const state = this.state;
const apple = this.state.apples;

如果你需要对你所引用的TYPE的状态执行操作,请在减速器中执行。

如果我说错了请指正!!