2015-11-19 6 views
3

Я делаю асинхронный вызов в своем создателе действия и вызываю свой редуктор с результатом, но по какой-то причине я не мог понять, что редуктор не называется.Реакция: создатель действия, не вызывающий редуктор

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) 

ответ

12

Есть несколько вещей здесь.

  1. Ваш создатель действия не возвращает действие, которое оно создает. $.get не возвращает действие, поэтому отображение его на dispatch не является продуктивным.

    Разумеется, это может привести к путанице в том, как вернуть действие от создателя действия async. Поскольку он является асинхронным, по своей природе он не может вернуть окончательный результат (если вы не используете es7 asyncawait). Вы пытаетесь образец идентичен этому:

    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 апи мы знаем, что $.get возвращает jqXHR object , а не какой-либо действия. Таким образом, вы должны использовать результат в обратном вызове самостоятельно, вы не можете просто вернуть его. Вот пример того, что componentDidMount может выглядеть так (обратите внимание на обратный вызов связан с this):

    componentDidMount() { 
        $.get(url, function(res) { 
        this.props.dispatch({ type: GET_FORMS, forms: res }); 
        }.bind(this)) 
    } 
    
  2. Ведение асинхр в компоненте или действия творца анти-паттерн, или, по крайней мере, не рекомендуется. В этом случае вы находитесь , определяя асинхронное действие внутри создателя действия, и это здорово. Но вы выполняете асинхронную операцию, а это плохо. Вместо этого вы должны использовать какое-то промежуточное программное обеспечение для обработки асинхронных операций. Middleware - это просто функции, которые предпринимают действия и делают с ними вещи после их отправки, а наиболее распространенным для этой цели является redux-thunk. Используя redux-thunk, вы просто возвращаете функцию, принимающую dispatch в качестве аргумента. В этом случае ваше действие создатель будет выглядеть следующим образом:

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

    Это очень похоже на то, что вы уже делаете, за исключением того, что вы создаете thunk --which это просто функция, которая определяет другую функцию для выполнения позже - вместо фактического выполнения операции async теперь, вы определяете его для выполнения позже.

  3. Вы на самом деле ничего не отправляете.

    Простейшая проблема для исправления, но определенно барьер :) В componentDidMount вы вызываете создателя действия, который вы импортировали, но вы не вызываете dispatch. Вы на 50% от того, что вы сделали, тем, что вы передали создателям действия connect, но даже если вы это сделали, вы не назвали создателей связанных действий, которые connect переходит в prop - вы называете несвязанные.

    Чтобы вместо звонка FormActions.getForms(), вам необходимо позвонить по телефону this.props.actions.formActions(). Первое не связано, но последнее. Вызов последнего совпадает с 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); 
    
+0

Спасибо для подробного объяснения того, как работает поток сокращений, я смог получить редуктор, захватив действие от создателя действия, и используя регистратор, я смог проверить, что есть изменение состояния. Но в методе рендеринга App.js формы array/object по-прежнему кажутся пустыми. Я делаю что-то неправильно в mapstatetoprops. –

+1

yes - mapStateToProps всегда будет возвращать пустой массив. Вы используете назначение destructuring, поэтому const {forms: forms} = {forms: []}; всегда будут form = []. Если вы используете соединение, вам нужно указать, какую часть глобального состояния вы хотите вводить в компонент. Подробнее об использовании можно прочитать здесь: http://rackt.org/redux/docs/basics/UsageWithReact.html –

+0

Yup. Я получил, что я не отображал формы в состояние и, следовательно, не смог получить изменения. Ссылка помогла мне отобразить мое состояние в формах и отобразить данные в моем методе рендеринга. –

4

Вы должны использовать Redux-санк среднего изделия: https://github.com/gaearon/redux-thunk

по существу, поскольку ваш код стоит - вы не возвращаете FSA-совместимое действие (и фактически ничего не возвращаете прямо сейчас). Вам нужно разгрузить отправку, а затем вернуть результат обещания.

Больше на создателях асинхронных действий здесь: http://redux.js.org/docs/advanced/AsyncActions.html

Смежные вопросы