【问题标题】:React : Action creator not calling reducer反应:动作创建者不调用减速器
【发布时间】:2015-11-19 16:29:21
【问题描述】:

我正在我的动作创建器中进行异步调用并使用结果调用我的减速器,但由于某种原因我无法理解减速器没有被调用。

actions.js(动作和动作创建者)

export const GET_FORMS = 'GET_FORMS'

export function getForms() {
  $.get("server url", function(result) {
   return {
    type: GET_FORMS,
    formlist: result.data.forms
   }   
  })
 }

reducers.js

import { GET_FORMS } from '../actions/actions'

export default function formAction(state = {forms:[]}, action) {
  console.log("received action"+action.type)
  switch (action.type) {

    case GET_FORMS:
      console.log("Action:"+action);
      return Object.assign({},state,{
       forms:action.formlist
      })

   default:
    return state
 }
}

App.js

import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import auth from '../auth'
import FormTable from '../components/FormTable'
import * as FormActions from '../actions/actions'


class App extends Component {

 constructor(props) {
  super(props);
  this.state = {forms: []};
 }

 componentDidMount() {
  // for some reason if i write this.props.actions.getForms () i get an error
  // Uncaught Error: Actions must be plain objects. Use custom middleware for 
  // async actions.
  FormActions.getForms();
 }

 render() {
  const {forms,actions} = this.props
  console.log(forms);
  return (
    <FormTable results={forms} actions = {actions} /> 
  )
 }
}

App.PropTypes = {
  forms: PropTypes.array.isRequired
}

function mapStateToProps() {
 const {
  forms:forms
 } = {forms:[]}

 return {
  forms
 }
}

function mapDispatchToProps(dispatch) {
 return {
  actions: bindActionCreators(FormActions, dispatch)
 }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

【问题讨论】:

    标签: javascript reactjs redux


    【解决方案1】:

    这里有一些东西。

    1. 您的动作创建者不会返回它正在创建的动作。 $.get 不返回操作,因此将其映射到 dispatch 并不高效。

      合理地,这可能会导致对如何从异步操作创建者返回操作感到困惑。由于它是异步的,本质上它不能返回最终结果(除非你使用的是 es7 async await)。您尝试的模式与此相同:

      class App extends Component {
        ...
        componentDidMount() {
          // Instead of calling getForms, do the same directly
          $.get(url, function(res) {
            return { type: GET_FORMS, forms: res };
          });
        }
      
        render() { ... }
      }
      

      $.get api 我们知道$.get 返回一个jqXHR object ,而不是任何形式的动作。所以你必须在回调中使用它自己的结果——你不能只返回它。下面是componentDidMount 的示例(注意回调绑定到this):

      componentDidMount() {
        $.get(url, function(res) {
          this.props.dispatch({ type: GET_FORMS, forms: res });
        }.bind(this))
      }
      
    2. 在您的组件或动作创建器中执行异步是一种反模式,或者至少不鼓励这样做。在这种情况下,您定义动作创建器中的异步,这很棒。但是您也在执行异步操作,这很糟糕。相反,您应该使用某种中间件来处理异步操作。中间件只是在调度后采取行动并对其进行处理的函数,最常见的用于此目的的是redux-thunk。使用redux-thunk,您只需返回一个接受dispatch 作为参数的函数。在这种情况下,您的动作创建者将如下所示:

      export function getForms() {
        return function(dispatch) {
          $.get(url, function(result) {
            dispatch({ type: GET_FORMS, forms: result });
          })
        }
      }
      

      这与你已经在做的非常相似,除了你正在创建一个thunk——它只是一个定义另一个函数以供稍后执行的函数——而不是实际执行异步操作 现在,您正在定义它以便稍后执行。

    3. 你实际上并没有发送任何东西。

      要解决的最简单的问题,但绝对是一个障碍 :) 在 componentDidMount 中,您正在调用已导入的动作创建者,但您没有调用 dispatch。虽然你已经完成了 50%,因为你将动作创建者传递给 connect,但即使你这样做了,你也没有调用 connect 传递给道具的绑定动作创建者——你调用了未绑定的。

      因此,您需要调用this.props.actions.formActions(),而不是调用FormActions.getForms()。前者不受约束,后者受约束。调用后者与调用this.props.dispatch(FormActions.getForms()) 相同。

    4. 最后:在这种情况下,您不需要定义 mapDispatchToProps。您可以将对象传递给connect 的该参数。见下文:

      // Instead of this
      export default connect(mapStateToProps, mapDispatchToProps)(App);
      
      // do this:
      export default connect(mapStateToProps, FormActions)(App);
      // creates the bound action creator 'this.props.getForms()'
      

      这主要是一种样式选择,但我认为这是一个很好的模式:) 对于多个动作创建者源对象,您可以这样做:

      export default connect(mapStateToProps, { ...FormActions, ...UserActions })(App);
      

    【讨论】:

    • 感谢有关 redux 流程如何工作的详细说明,我能够让 reducer 从动作创建者那里捕获动作,并使用记录器来验证状态是否发生变化。但是在 App.js 的 render 方法中,表单数组/对象似乎仍然是空的。我在 mapstatetoprops 中做错了吗?
    • 是的 - mapStateToProps 将始终返回一个空数组。您正在使用解构赋值,因此 const { forms: forms } = { forms: []};将始终是表单 = []。如果您使用的是连接,则需要指定要将全局状态的哪一部分注入到组件中。您可以在此处阅读有关使用 react 的更多信息:rackt.org/redux/docs/basics/UsageWithReact.html
    • 是的。我知道我没有将表单映射到状态,因此无法获得更改。该链接帮助我将状态映射到表单并在我的渲染方法中显示获取数据。
    • 谢谢谢谢谢谢!这个解释非常棒,非常有帮助。从文档中学习是一回事,但通过深思熟虑地回顾某人的尝试来学习是另一回事。 ...我很欣赏你不带偏见、乐于助人的语气。真的。这篇文章把我从,“嗯?” to r e d u x w o r k i n g (yeah)
    【解决方案2】:

    你应该使用 redux-thunk 中间件: https://github.com/gaearon/redux-thunk

    基本上就像您的代码一样 - 您没有返回符合 FSA 的操作(实际上现在没有返回任何内容)。你需要对 dispatch 进行 thunk,然后返回 promise 的结果。

    更多关于异步动作创建者的信息:http://redux.js.org/docs/advanced/AsyncActions.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 2018-02-02
      • 1970-01-01
      • 2021-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多