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

IT技术 javascript redux
2021-02-06 23:16:02

说我有以下几点:

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));
  }
}
6个回答

关于在 action creators 中访问 state 是否是一个好主意,有不同的意见:

  • Redux 创建者 Dan Abramov 认为它应该受到限制:“我认为可以接受的少数用例是在您发出请求之前检查缓存数据,或者检查您是否通过身份验证(换句话说,进行有条件的调度)。我认为,通过数据state.something.items在行动的创建者绝对是一个反模式和气馁,因为它掩盖了改变历史:如果有一个错误,items是不正确的,这是很难追查,其中那些不正确的值来自于,因为它们是已经是动作的一部分,而不是由减速器直接计算以响应动作。所以要小心。”
  • 当前 Redux 维护者 Mark Erikson 说它很好,甚至鼓励getState在 thunk 中使用- 这就是它存在的原因他在他的博客文章Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability 中讨论了在 action creators 中访问 state 的利弊

如果你发现你需要这个,你建议的两种方法都很好。第一种方法不需要任何中间件:

import store from '../store';

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

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

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

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

    dispatch(anotherAction(items));
  }
}

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

理想情况下,你的动作不应该是“胖”的,应该包含尽可能少的信息,但你应该在自己的应用程序中自由地做最适合你的事情。的终极版FAQ对信息行动的创作者和减速器之间拆分逻辑时间时,它可以是使用有用的getState一个动作的创造者

我有一种情况,在组件中选择某些内容可能会触发 PUT 或 POST,具体取决于商店是否包含与组件相关的数据。将 PUT/POST 选择的业务逻辑放在组件中而不是基于 thunk 的动作创建器是否更好?
2021-03-12 23:16:02
最佳做法是什么?我现在面临着类似的问题,我在我的动作创建者中使用 getState。在我的情况下,我使用它来确定表单是否有待处理的更改的值(如果是,我将发送一个显示对话框的操作)。
2021-03-16 23:16:02
可以在 action creator 中从 store 中读取。我鼓励您使用选择器,这样您就不必依赖于确切的状态形状。
2021-03-21 23:16:02
天啊!我没有把那个 redux thunk 接收getState为第二个参数,我正在破解我的头,非常感谢你
2021-03-23 23:16:02
我正在使用中间件将数据发送到 mixpanel。所以我在动作中有一个元键。我需要将不同的变量从状态传递到混合面板。将它们设置在动作创建者身上似乎是一种反模式。处理此类用例的最佳方法是什么?
2021-03-30 23:16:02

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

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

注意:你需要redux-thunk在 action creator 中返回函数

我可以问一下是否应该检查状态“加载”的状态,以便在第一个完成时不会发出另一个 ajax 请求?
2021-03-23 23:16:02
@JoeTidee 在示例中,加载状态的分派已完成。例如,如果您使用按钮执行此操作,您将检查是否loading === true存在并禁用该按钮。
2021-03-31 23:16:02

我同意@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);
这是正确的做法以及我的做法。你的动作创建者不需要知道整个状态,只需要知道与其相关的部分。
2021-03-12 23:16:02
这种方法是非常合乎逻辑的。
2021-04-09 23:16:02

我想指出,从 store 读取并没有那么糟糕——根据 store 决定应该做什么可能比将所有内容传递给组件然后作为参数更方便一个函数。我完全同意 Dan 的观点,最好不要将 store 用作单调,除非您 100% 确定将仅用于客户端渲染(否则可能会出现难以追踪的错误)。

最近创建了一个库来处理 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())
    };
  },
});

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

提出解决此问题的替代方法。这可能比 Dan 的解决方案更好或更差,具体取决于您的应用。

您可以通过将操作拆分为 2 个单独的函数来将来自 reducer 的状态转换为操作:首先请求数据,然后对数据进行操作。您可以使用redux-loop.

首先“请提供数据”

export const SOME_ACTION = 'SOME_ACTION';
export function someAction() {
    return {
        type: SOME_ACTION,
    }
}

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

有了手头的数据,做任何你最初想做的事

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

希望这可以帮助某人。