2016-08-02 60 views
12

我正在使用ReactJS + Redux以及Express和Webpack。建立了一個API,我希望能夠從客戶端進行REST調用 - GET,POST,PUT,DELETE。如何從ReactJS + Redux應用程序正確地創建REST調用?

在Redux架構下如何以及如何正確實現?就減速器,動作創造者,商店和反應路線而言,流動的任何好例子都是非常有用的。

預先感謝您!

回答

10

最簡單的方法,就是用redux-thunk包來做。這個包是一個終極版中間件,所以首先,你應該把它連接到終極版:

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 
}); 

對於其他操作,如CREATEUPDATEDELETE沒有什麼特別的,他們正在實施方式相同。

+0

非常感謝您的幫助。仍然試圖抓住這個概念。你如何以及在哪裏從組件中調用動作? (({response,body})=> {},然後你可以澄清一下'.then(response => response.json()。then(body =>({response,body}))).then '在做什麼?再次感謝 –

+0

@JoKo,是的,我很快就會更新答案。 – 1ven

+0

@JoKo,更新回答 – 1ven

0

這是庫的主要用例,如redux-thunk,redux-sagaredux-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究竟做了什麼特別的事情呢?看起來像你可以只是簡單地做到沒有任何庫的API()到API的URL。 –

+0

'redux-thunk'在體系結構上非常有用,可將異步行爲集成到同步的redux中。 'fetch'就足夠網絡電話了,但那很容易。當你開始詢問如何通過redux應用程序進行調用時,你需要像'redux-thunk'這樣的東西將這種行爲合併到你的redux體系結構中。 –

+0

真的很感謝澄清!換句話說,GET是否正確?那麼,POST,PUT和DELETE會是什麼? –

1

簡短的回答是:

  1. 終極版不是一個架構
  2. 你可以使用任何庫。現在很多人直接使用獲取API。
  3. 爲了能夠將redux與異步操作(您需要用於AJAX)進行集成,您需要使用庫來提供幫助。正如其他人所說,最受歡迎的兩個是redux-thunkredux-saga

對於您可以放入您的REDX應用程序的大腦死亡簡單的庫,您可以嘗試redux-crud-store。免責聲明:我寫了。您也可以閱讀redux-crud-store的源代碼,如果您有興趣將fetch API或其他API客戶端與redux-saga集成在一起

相關問題