2016-08-02 2 views
12

Я использую ReactJS + Redux вместе с Express и Webpack. Существует встроенный API, и я хочу иметь возможность делать вызовы REST - GET, POST, PUT, DELETE - с клиентской стороны.Как правильно делать вызовы REST из приложения ReactJS + Redux?

Как и как правильно это сделать с архитектурой Redux? Любой хороший пример потока, с точки зрения редукторов, создателей действий, хранения и реагирования на маршруты, был бы чрезвычайно полезен.

Заранее благодарю вас!

ответ

10

Проще всего это сделать, используя redux-thunk. Этот пакет является перевождь промежуточного слоя, поэтому в первую очередь, вы должны подключить его к Redux:

import { createStore, applyMiddleware } from 'redux'; 
import thunk from 'redux-thunk'; 
import rootReducer from './reducers/index'; 

const store = createStore(
    rootReducer, 
    applyMiddleware(thunk) 
); 

Это позволяет направить async действия наряду с регулярными sync действиями. Давайте создадим один из них:

// actions.js 

export function fetchTodos() { 
    // Instead of plain objects, we are returning function. 
    return function(dispatch) { 
    // Dispatching REQUEST action, which tells our app, that we are started requesting todos. 
    dispatch({ 
     type: 'FETCH_TODOS_REQUEST' 
    }); 
    return fetch('/api/todos') 
     // Here, we are getting json body(in our case it will contain `todos` or `error` prop, depending on request was failed or not) from server response 
     // And providing `response` and `body` variables to the next chain. 
     .then(response => response.json().then(body => ({ response, body }))) 
     .then(({ response, body }) => { 
     if (!response.ok) { 
      // If request was failed, dispatching FAILURE action. 
      dispatch({ 
      type: 'FETCH_TODOS_FAILURE', 
      error: body.error 
      }); 
     } else { 
      // When everything is ok, dispatching SUCCESS action. 
      dispatch({ 
      type: 'FETCH_TODOS_SUCCESS', 
      todos: body.todos 
      }); 
     } 
     }); 
    } 
} 

Я предпочитаю разделять реагирующие компоненты на презентационные и контейнерные компоненты. Этот подход был полностью описан в this article.

Далее мы должны создать компонент TodosContainer, который предоставит данные представительному компоненту Todos. Здесь мы используем react-redux библиотеку:

// TodosContainer.js 

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { fetchTodos } from '../actions'; 

class TodosContainer extends Component { 
    componentDidMount() { 
    // When container was mounted, we need to start fetching todos. 
    this.props.fetchTodos(); 
    } 

    render() { 
    // In some simple cases, it is not necessary to create separate `Todos` component. You can put todos markup directly here. 
    return <Todos items={this.props.todos} /> 
    } 
} 

// This function is used to convert redux global state to desired props. 
function mapStateToProps(state) { 
    // `state` variable contains whole redux state. 
    return { 
    // I assume, you have `todos` state variable. 
    // Todos will be available in container component as `this.props.todos` 
    todos: state.todos 
    }; 
} 

// This function is used to provide callbacks to container component. 
function mapDispatchToProps(dispatch) { 
    return { 
    // This function will be available in component as `this.props.fetchTodos` 
    fetchTodos: function() { 
     dispatch(fetchTodos()); 
    } 
    }; 
} 

// We are using `connect` function to wrap our component with special component, which will provide to container all needed data. 
export default connect(mapStateToProps, mapDispatchToProps)(TodosContainer); 

Кроме того, вы должны создать todosReducer, который будет обрабатывать FETCH_TODOS_SUCCESS действие, а другие 2 действия, если вы хотите, сообщение дисплей Загрузчик/ошибка.

// reducers.js 

import { combineReducers } from 'redux'; 

const INITIAL_STATE = { 
    items: [], 
    isFetching: false, 
    error: undefined 
}; 

function todosReducer(state = INITIAL_STATE, action) { 
    switch (action.type) { 
    case 'FETCH_TODOS_REQUEST': 
     // This time, you may want to display loader in the UI. 
     return Object.assign({}, state, { 
     isFetching: true 
     }); 
    case 'FETCH_TODOS_SUCCESS': 
     // Adding derived todos to state 
     return Object.assign({}, state, { 
     isFetching: false, 
     todos: action.todos 
     }); 
    case 'FETCH_TODOS_FAILURE': 
     // Providing error message to state, to be able display it in UI. 
     return Object.assign({}, state, { 
     isFetching: false, 
     error: action.error 
     }); 
    default: 
     return state; 
    } 
} 

export default combineReducers({ 
    todos: todosReducer 
}); 

Для других операций, как CREATE, UPDATE, DELETE нет ничего особенного, они осуществляют таким же образом.

+0

Большое вам спасибо за помощь. все еще пытаясь понять концепцию. как и где вы называете действие от компонента? Также (body => ({response, body}))) . then (({response, body}) => {{response, body}) => { 'делаю? Еще раз спасибо –

+0

@JoKo, да, я скоро обновлю ответ. – 1ven

+0

@JoKo, обновленный ответ – 1ven

0

Это основной вариант использования библиотек, таких как redux-thunk, redux-saga и redux-observable.

redux-thunk является самым простым, где вы могли бы сделать что-то вроде этого:

import fetch from 'isomorphic-fetch' 

export const REQUEST_POSTS = 'REQUEST_POSTS' 
function requestPosts(subreddit) { 
    return { 
    type: REQUEST_POSTS, 
    subreddit 
    } 
} 

export const RECEIVE_POSTS = 'RECEIVE_POSTS' 
function receivePosts(subreddit, json) { 
    return { 
    type: RECEIVE_POSTS, 
    subreddit, 
    posts: json.data.children.map(child => child.data), 
    receivedAt: Date.now() 
    } 
} 

// Meet our first thunk action creator! 
// Though its insides are different, you would use it just like any other action creator: 
// store.dispatch(fetchPosts('reactjs')) 

export function fetchPosts(subreddit) { 

    // Thunk middleware knows how to handle functions. 
    // It passes the dispatch method as an argument to the function, 
    // thus making it able to dispatch actions itself. 

    return function (dispatch) { 

    // First dispatch: the app state is updated to inform 
    // that the API call is starting. 

    dispatch(requestPosts(subreddit)) 

    // The function called by the thunk middleware can return a value, 
    // that is passed on as the return value of the dispatch method. 

    // In this case, we return a promise to wait for. 
    // This is not required by thunk middleware, but it is convenient for us. 

    return fetch(`http://www.reddit.com/r/${subreddit}.json`) 
     .then(response => response.json()) 
     .then(json => 

     // We can dispatch many times! 
     // Here, we update the app state with the results of the API call. 

     dispatch(receivePosts(subreddit, json)) 
    ) 

     // In a real world app, you also want to 
     // catch any error in the network call. 
    } 
} 

Приведенный выше пример взят непосредственно из http://redux.js.org/docs/advanced/AsyncActions.html, который является действительно окончательным источником для ответов на ваш вопрос.

+0

Что делает Thunk именно так особенным? Похоже, вы просто можете сделать fetch() по URL API без каких-либо библиотек. –

+0

'redux-thunk' является архитектурно полезным для интеграции асинхронного поведения в редукцию, которая является синхронной. 'fetch' достаточно для совершения сетевых вызовов, но это легкая часть. Когда вы начинаете спрашивать о том, как делать вызовы из приложения redux, вам нужно что-то вроде 'redux-thunk', чтобы включить это поведение в вашу архитектуру сокращения. –

+0

действительно ценю разъяснение! другими словами, GET правильно? Тогда что будет POST, PUT и DELETE? –

1

Короткий ответ:

  1. перевождь не архитектура
  2. Вы можете использовать любую библиотеку. В наше время многие люди используют API-интерфейс fetch.
  3. Чтобы иметь возможность интегрировать сокращение с асинхронными действиями (которые вам нужны для AJAX), вам нужно использовать библиотеку, чтобы помочь. Самые популярные два - redux-thunk и redux-saga, как говорили другие.

Для простой библиотеки с мозгом, которую вы можете зайти в приложение для редукции, вы можете попробовать redux-crud-store. Отказ от ответственности: Я написал это.Вы также можете прочитать источник для redux-crud-store, если вы заинтересованы в интеграции API-интерфейса fetch или другого клиента API с сокращением saga.

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