2016-08-02 23 views
12

Tôi đang sử dụng ReactJS + Redux, cùng với Express và Webpack. Có một API được xây dựng và tôi muốn có thể thực hiện các cuộc gọi REST - GET, POST, PUT, DELETE - từ phía máy khách.Làm thế nào để thực hiện đúng các cuộc gọi REST từ ứng dụng ReactJS + Redux?

Cách và cách thức phù hợp để thực hiện điều đó với kiến ​​trúc Redux là gì? Bất kỳ ví dụ điển hình nào về dòng chảy, về mặt giảm tốc, người tạo hành động, lưu trữ và các tuyến phản ứng, sẽ cực kỳ hữu ích.

Cảm ơn bạn trước!

Trả lời

10

Cách đơn giản nhất, là sử dụng gói redux-thunk. Gói này là một middleware Redux, vì vậy trước hết, bạn nên kết nối nó với Redux:

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

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

này cho phép bạn để gửi async hành động cùng với thường xuyên sync hành động. Hãy tạo một trong số chúng:

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

Tôi thích tách riêng các thành phần phản ứng trên các thành phần hiện tại và thùng chứa. Cách tiếp cận này được mô tả một cách hoàn hảo trong this article.

Tiếp theo, chúng ta nên tạo thành phần TodosContainer, sẽ cung cấp dữ liệu cho thành phần Todos hiện tại. Ở đây, chúng ta đang sử dụng react-redux thư viện:

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

Ngoài ra, bạn nên tạo todosReducer, mà sẽ xử lý FETCH_TODOS_SUCCESS hành động, và 2 hành động khác nếu bạn muốn hiển thị tin nhắn nạp/lỗi.

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

Đối với các hoạt động khác như CREATE, UPDATE, DELETE không có gì đặc biệt, họ đang thực hiện theo cùng một cách là.

+0

cảm ơn bạn rất nhiều vì đã giúp đỡ. vẫn cố gắng nắm bắt khái niệm. bạn gọi hành động từ một thành phần như thế nào và ở đâu? bạn cũng có thể làm rõ thêm một chút về '.then (response => response.json(). sau đó (body => ({response, body}))) .then (({response, body}) => { 'đang làm gì? Cảm ơn một lần nữa –

+0

@ JoKo, vâng, tôi sẽ cập nhật câu trả lời sớm. – 1ven

+0

@ JoKo, câu trả lời cập nhật – 1ven

0

Đây là trường hợp sử dụng chính cho các thư viện như redux-thunk, redux-sagaredux-observable.

redux-thunk là đơn giản nhất, nơi bạn sẽ làm điều gì đó như thế này:

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. 
    } 
} 

Ví dụ trên được lấy trực tiếp từ http://redux.js.org/docs/advanced/AsyncActions.html mà thực sự là nguồn dứt khoát cho câu trả lời về câu hỏi của bạn.

+0

Thunk chính xác làm gì đặc biệt về nó? Có vẻ như bạn chỉ có thể thực hiện fetch() vào URL của API mà không cần bất kỳ thư viện nào. –

+0

'redux-thunk' hữu dụng về mặt kiến ​​trúc để tích hợp hành vi không đồng bộ vào redux, đồng bộ. 'fetch' là đủ để thực hiện cuộc gọi mạng, nhưng đó là phần dễ dàng. Khi bạn bắt đầu hỏi về cách thực hiện cuộc gọi từ một ứng dụng redux, bạn cần một cái gì đó như 'redux-thunk' để kết hợp hành vi đó vào kiến ​​trúc redux của bạn. –

+0

thực sự đánh giá cao việc làm rõ! tìm nạp là nói cách khác GET đúng? Sau đó, POST, PUT và DELETE sẽ là gì? –

1

Câu trả lời ngắn gọn là:

  1. Redux không phải là một kiến ​​trúc
  2. Bạn có thể sử dụng bất kỳ thư viện. Rất nhiều người trong những ngày này sử dụng API tìm nạp trực tiếp.
  3. Để có thể tích hợp Redux với các hành động không đồng bộ (mà bạn cần cho AJAX), bạn cần sử dụng thư viện để trợ giúp. Hai loại phổ biến nhất là redux-thunkredux-saga, như những người khác đã nói.

Để thư viện đơn giản chết não mà bạn có thể đăng nhập vào ứng dụng redux, bạn có thể thử redux-crud-store. Disclaimer: Tôi đã viết nó.Bạn cũng có thể đọc nguồn cho redux-crud-store nếu bạn quan tâm đến việc tích hợp API tìm nạp hoặc một ứng dụng API khác, với redux-saga

Các vấn đề liên quan