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

时间:2016-02-27 08:00:19

标签: javascript redux

说我有以下内容:

ActivityType

在该动作创建者中,我想访问全局存储状态(所有reducers)。这样做更好:

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,
  }
}

8 个答案:

答案 0 :(得分:397)

关于在动作创作者中访问状态是否是一个好主意,存在不同的意见。我认为可接受的少数用例是在您发出请求之前检查缓存数据,或者检查您是否经过身份验证(换句话说,执行条件派遣)。我认为在动作创建者中传递数据(例如state.something.items)肯定是一种反模式而不鼓励,因为它模糊了变更历史:如果存在错误且items如果不正确,很难跟踪 这些不正确的值来自哪里,因为它们已经是动作的一部分,而不是由reducer直接计算以响应动作。所以要小心这样做。 (有关在动作创建者中访问状态的利弊的进一步讨论,请参阅博客文章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,
  }
}

但是你可以看到它依赖store作为从某个模块导出的单例。 我们不建议,因为它会让add server rendering to your app变得更加困难,因为在大多数情况下服务器上的您希望每个请求都有一个单独的商店 。因此,虽然从技术上讲这种方法有效,但我们不建议从模块中导出商店。

这就是我们推荐第二种方法的原因:

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
  return (dispatch, getState) => {
    const {items} = getState().otherReducer;

    dispatch(anotherAction(items));
  }
}

它需要您使用Redux Thunk中间件,但它在客户端和服务器上都可以正常工作。在这种情况下,您可以阅读有关Redux Thunk和为什么的更多内容here

理想情况下,您的行为不应该“胖”,并且应尽可能少地包含信息,但您应该随意在自己的应用程序中做最适合您的操作。 Redux常见问题解答包含有关splitting logic between action creators and reducerstimes when it may be useful to use getState in an action creator的信息。

答案 1 :(得分:24)

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

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次行动

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

注意:您需要http://www.jmlr.org/papers/volume5/evendar03a/evendar03a.pdf才能在动作创建者中返回功能

答案 2 :(得分:3)

我想指出,从商店中读取并不是那么糟糕 - 根据商店决定应该做什么可能更方便,而不是将所有内容传递给组件然后作为函数的参数。我完全同意Dan的观点,最好不要将商店用作单机版,除非你100%确定你只会用于客户端渲染(否则很难跟踪错误)。

I have created a library最近要处理redux的冗长问题,我认为将所有东西都放在中间件中是个好主意,所以你要将allhing作为依赖注入。

因此,您的示例将如下所示:

import { createSyncTile } from 'redux-tiles';

const someTile = createSyncTile({
  type: ['some', 'tile'],
  fn: ({ params, selectors, getState }) => {
    return {
      data: params.data,
      items: selectors.another.tile(getState())
    };
  },
});

但是,正如您所看到的,我们并未真正修改数据,因此我们很有可能只在其他地方使用此选择器将其组合到其他位置。

答案 3 :(得分:2)

我同意@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);

答案 4 :(得分:1)

提出一种解决此问题的替代方法。取决于您的应用程序,这可能比Dan的解决方案更好或更糟。

您可以通过将动作分为两个单独的功能来将化简器的状态转换为动作:首先索要数据,其次对数据进行动作。您可以使用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,
    }
}

希望这对某人有帮助。

答案 5 :(得分:0)

我知道我在这里参加晚会很晚,但是我来到这里是出于自己对在行动中运用国家的渴望的见解,然后当我意识到自己认为是正确的行为时就形成了自己的观点。

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

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

这听起来像是泄漏了抽象,但是您的组件显然需要发送一条消息,并且消息有效负载应包含相关状态。不幸的是,您的问题没有具体的示例,因为我们可以通过这种更好的选择器和操作模型来工作。

答案 6 :(得分:0)

我想提出另一个我认为最干净的替代方法,但是它需要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提取到函数中以在其他地方重用,但这使依赖关系非常清晰。

答案 7 :(得分:0)

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

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

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

如果您需要对要引用的TYPE的状态执行操作,请在化简器中进行。

如果我错了,请指正!!