Javascript 如何从 ReactJS + Redux 应用程序正确地进行 REST 调用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38728884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to properly make REST calls from ReactJS + Redux application?
提问by Jo Ko
I'm using ReactJS + Redux, along with Express and Webpack. There is an API built, and I want to be able to make REST calls -- GET, POST, PUT, DELETE -- from the client-side.
我正在使用 ReactJS + Redux,以及 Express 和 Webpack。构建了一个 API,我希望能够从客户端进行 REST 调用——GET、POST、PUT、DELETE。
How and what is the properly way to go about doing so with the Redux architecture? Any good example of the flow, in terms of reducers, action creators, store, and react routes, would be extremely helpful.
使用 Redux 架构如何以及如何正确地做到这一点?任何好的流程示例,在减速器、动作创建器、存储和反应路线方面,都会非常有帮助。
Thank you in advance!
先感谢您!
回答by 1ven
The simpliest way, is to do it using redux-thunk
package. This package is an redux middleware, so first of all, you should connect it to redux:
最简单的方法是使用 package.json 来完成redux-thunk
。这个包是一个 redux 中间件,所以首先,你应该把它连接到 redux:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
This allows you to dispatch async
actions along with regular sync
actions. Let's create one of them:
这允许您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
});
}
});
}
}
I prefer to separate react components on presentational and container components. This approach was perfectly described in this article.
我更喜欢将展示组件和容器组件上的反应组件分开。这篇文章完美地描述了这种方法。
Next, we should create TodosContainer
component, which would provide data to presentational Todos
component. Here, we are using react-redux
library:
接下来,我们应该创建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);
Also, you should create todosReducer
, which will handle FETCH_TODOS_SUCCESS
action, and other 2 actions if you want display loader / error message.
此外,如果您想显示加载器/错误消息,您应该创建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
});
For other operations like CREATE
, UPDATE
, DELETE
there is nothing special, they are implementing the same way.
对于其它操作喜欢CREATE
,UPDATE
,DELETE
没有什么特别的,他们正在实施方式相同。
回答by Devin Howard
The short answer is:
简短的回答是:
- redux is not an architecture
- You can use any library. A lot of people these days use the fetch API directly.
- To be able to integrate redux with asynchronous actions (which you need for AJAX), you need to use a library to help. The most popular two are
redux-thunk
andredux-saga
, as others have said.
- redux 不是架构
- 您可以使用任何库。现在很多人直接使用 fetch API。
- 为了能够将 redux 与异步操作(AJAX 所需)集成,您需要使用一个库来提供帮助。正如其他人所说,最受欢迎的两个是
redux-thunk
和redux-saga
。
For a brain-dead simple library that you can drop in to your redux app, you could try redux-crud-store. Disclaimer: I wrote it. You could also read the source for redux-crud-store if you are interested in integrating the fetch API, or another API client, with redux-saga
对于可以放入 redux 应用程序的脑残简单库,您可以尝试redux-crud-store。免责声明:我写的。如果您有兴趣将 fetch API 或其他 API 客户端与 redux-saga 集成,您还可以阅读 redux-crud-store 的源代码
回答by Nathan Hagen
This is the primary use case for libraries like redux-thunk
, redux-saga
, and redux-observable
.
这是主用例等的库redux-thunk
,redux-saga
和redux-observable
。
redux-thunk
is the simplest, where you would do something like this:
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.
}
}
The above example is taken directly from http://redux.js.org/docs/advanced/AsyncActions.htmlwhich is really the definitive source for answers on your question.
上面的示例直接取自http://redux.js.org/docs/advanced/AsyncActions.html,它确实是您问题答案的权威来源。