React:动作创建者不调用减速器

React : Action creator not calling reducer

我正在我的 action creator 中进行异步调用,并用结果调用我的 reducer,但出于某种原因,我无法理解没有调用 reducer。

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)

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

基本上就您的代码而言 - 您没有 return 执行符合 FSA 的操作(实际上现在没有 return 执行任何操作)。您需要对调度进行 thunk,然后 return 承诺的结果。

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

这里有一些东西。

  1. 您的动作创建器不会 return 它正在创建的动作。 $.get 没有 return 一个动作,因此将它映射到 dispatch 是没有效率的。

    合理地,这可能会导致对如何 return 来自异步动作创建者的动作感到困惑。由于它是异步的,本质上它不能 return 最终结果(除非你使用的是 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 return 是 jqXHR object ,而不是任何一种行动。所以你必须在回调中使用它自己的结果——你不能只 return 它。下面是 componentDidMount 的示例(注意回调绑定到 this):

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

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

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

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

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

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

  4. 相同
  5. 最后:在这种情况下您不需要定义 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);