web-dev-qa-db-fra.com

Comment faire une requête AJAX sous redux

Pour tout ce que je sais, je dois écrire une demande en action créer. Comment utiliser une promesse en action pour soumettre une demande? Je reçois des données en action. Ensuite, le nouvel état est créé dans le réducteur. Action de reliure et réducteur dans connect. Mais je ne sais pas comment utiliser promesse pour demande.

Action

import $ from 'jquery';
export const GET_BOOK = 'GET_BOOK';

export default function getBook() {
  return {
    type: GET_BOOK,
    data: $.ajax({
      method: "GET",
      url: "/api/data",
      dataType: "json"
    }).success(function(data){
      return data;
    })
  };
}

Réducteur

import {GET_BOOK} from '../actions/books';

const booksReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_BOOK:
      return state;
    default:
      return state;
  }
};

export default booksReducer;

Container Comment afficher les données dans un conteneur?

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import getBook  from '../actions/books';
import Radium from 'radium';
import {Link} from 'react-router';

function mapStateToProps(state) {
  return {
    books: state.data.books,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    getBooks: () => dispatch(getBook()),
  };
}

@Radium
@connect(mapStateToProps, mapDispatchToProps)
class booksPage extends Component {
  static propTypes = {
    getBooks: PropTypes.func.isRequired,
    books: PropTypes.array.isRequired,
  };

  render() {
    const {books} = this.props;
    return (
      <div>
        <Link to={`/authors`}><MUIButton style="flat">All Authors</MUIButton></Link>
        <ul>
          {books.map((book, index) =>
            <li key={index}>
              <Link to={`/book/${book.name}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
                "{book.name}"</div></MUIButton></Link>
              <Link to={`/author/${book.author}`}><MUIButton style="flat"><div class="mui--text-black mui--text-display4">
                {book.author}</div></MUIButton></Link>
            </li>
          )}
        </ul>
      </div>
    );
  }
}

export default booksPage;
62
Disa Skolzin

Puisque vous utilisez déjà redux, vous pouvez appliquer redux-thunk middleware permettant de définir des actions asynchrones.

Installation & utilisation: Redux-thunk

export function fetchBook(id) {
 return dispatch => {
   dispatch(setLoadingBookState()); // Show a loading spinner
   fetch(`/book/${id}`, (response) => {
     dispatch(doneFetchingBook()); // Hide loading spinner
     if(response.status == 200){
       dispatch(setBook(response.json)); // Use a normal function to set the received state
     }else { 
       dispatch(someError)
     }
   })
 }
}

function setBook(data) {
 return { type: 'SET_BOOK', data: data };
}
54
JensDebergh

Vous devez utiliser les actions asynchrones décrites dans Documentation Redux

Voici un exemple de réducteur pour une action asynchrone.

const booksReducer = (state = {}, action) => {
  switch (action.type) {
    case 'RESOLVED_GET_BOOK':
      return action.data;
    default:
      return state;
  }
};

export default booksReducer;

et ensuite vous créez votre action asynchrone.

export const getBook() {
  return fetch('/api/data')
    .then(response => response.json())
    .then(json => dispatch(resolvedGetBook(json)))
}

export const resolvedGetBook(data) {
  return {
    type: 'RESOLVED_GET_BOOK',
    data: data
  }
}

Plusieurs notes:

  • Nous pourrions retourner Promise (au lieu de Object) en action en utilisant le middleware redux-thunk.
  • N'utilisez pas la bibliothèque ajax jQuery. Utilisez une autre bibliothèque spécialement pour cela (par exemple, fetch ()). J'utilise client HTTP axios .
  • Rappelez-vous que dans redux, vous n’utilisez que la fonction pure dans réducteur. Ne faites pas d'appel ajax à l'intérieur du réducteur.
  • Lire le guide complet de redux docs.
26
Kiddo

Vous devriez pouvoir utiliser dispatch à l'intérieur du rappel (si vous le transmettez en tant qu'argument):

export default function getBook(dispatch) {
  $.ajax({
      method: "GET",
      url: "/api/data",
      dataType: "json"
    }).success(function(data){
      return dispatch({type:'GET_BOOK', data: data});
    });
}

Ensuite, passez dispatch à l'action:

function mapDispatchToProps(dispatch) {
  return {
    getBooks: () => getBook(dispatch),
  };
}

Maintenant, vous devriez avoir accès à la action.data propriété dans le réducteur:

const booksReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_BOOK:
      //action.data  <--- here
      return state;
    default:
      return state;
  }
};
13
Davin Tryon

Vous voudrez peut-être séparer les préoccupations, afin de garder les créateurs d'action "purs".

Solution; écrire un middleware. Prenez ceci par exemple (en utilisant superagent).

import Request from 'superagent';

const successHandler = (store,action,data) => {

    const options = action.agent;
    const dispatchObject = {};
    dispatchObject.type = action.type + '_SUCCESS';
    dispatchObject[options.resourceName || 'data'] = data;
    store.dispatch(dispatchObject);
};

const errorHandler = (store,action,err) => {

    store.dispatch({
        type: action.type + '_ERROR',
        error: err
    });
};

const request = (store,action) => {

    const options = action.agent;
    const { user } = store.getState().auth;
    let method = Request[options.method];

    method = method.call(undefined, options.url)

    if (user && user.get('token')) {
        // This example uses jwt token
        method = method.set('Authorization', 'Bearer ' + user.get('token'));
    }

    method.send(options.params)
    .end( (err,response) => {
        if (err) {
            return errorHandler(store,action,err);
        }
        successHandler(store,action,response.body);
    });
};

export const reduxAgentMiddleware = store => next => action => {

    const { agent } = action;

    if (agent) {
        request(store, action);
    }
    return next(action);
};

Mettez tout cela dans un module.

Maintenant, vous pourriez avoir un créateur d'action appelé 'auth':

export const auth = (username,password) => {

    return {
        type: 'AUTHENTICATE',
        agent: {
            url: '/auth',
            method: 'post',
            resourceName: 'user',
            params: {
                username,
                password
            }
        }
    };
};

La propriété 'agent' sera récupérée par le middleware, qui envoie la requête construite sur le réseau, puis envoie le résultat entrant à votre magasin.

Votre réducteur gère tout cela après avoir défini les crochets:

import { Record } from 'immutable';

const initialState = Record({
    user: null,
    error: null
})();

export default function auth(state = initialState, action) {

    switch (action.type) {

        case 'AUTHENTICATE':

            return state;

        case 'AUTHENTICATE_SUCCESS':

            return state.merge({ user: action.user, error: null });

        case 'AUTHENTICATE_ERROR':

            return state.merge({ user: null, error: action.error });

        default:

            return state;
    }
};

Maintenant, injectez tout cela dans votre logique de vue. J'utilise réagir comme exemple.

import React from 'react';
import ReactDOM from 'react-dom';

/* Redux + React utils */
import { createStore, applyMiddleware, bindActionCreators } from 'redux';
import { Provider, connect } from 'react-redux';

// thunk is needed for returning functions instead 
// of plain objects in your actions.
import thunkMiddleware from 'redux-thunk';

// the logger middleware is useful for inspecting data flow
import createLogger from 'redux-logger';

// Here, your new vital middleware is imported
import { myNetMiddleware } from '<your written middleware>';

/* Vanilla index component */
import _Index from './components';

/* Redux reducers */
import reducers from './reducers';

/* Redux actions*/
import actionCreators from './actions/auth';


/* create store */
const store = createStore(
    reducers,
    applyMiddleware(
        thunkMiddleware,
        myNetMiddleware
    )
);

/* Taint that component with store and actions */
/* If all goes well props should have 'auth', after we are done */
const Index = connect( (state) => {

    const { auth } = state;

    return {
        auth
    };
}, (dispatch) => {

    return bindActionCreators(actionCreators, dispatch);
})(_Index);

const provider = (
    <Provider store={store}>
        <Index />
    </Provider>
);

const entryElement = document.getElementById('app');
ReactDOM.render(provider, entryElement);

Tout cela implique que vous ayez déjà configuré un pipeline en utilisant webpack, rollup ou autre, pour transpiler d’es2015 et réagir à Vanilla js.

7
Thomas E