如何正确地从 ReactJS + Redux 应用程序进行 REST 调用?
Posted
技术标签:
【中文标题】如何正确地从 ReactJS + Redux 应用程序进行 REST 调用?【英文标题】:How to properly make REST calls from ReactJS + Redux application? 【发布时间】:2016-12-08 07:21:56 【问题描述】:我正在使用 ReactJS + Redux,以及 Express 和 Webpack。构建了一个 API,我希望能够从客户端进行 REST 调用——GET、POST、PUT、DELETE。
使用 Redux 架构如何以及正确的方法是什么?在 reducer、action creators、store 和 react 路由方面,任何好的流程示例都会非常有帮助。
提前谢谢你!
【问题讨论】:
【参考方案1】:最简单的方法是使用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)
);
这允许您调度 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
);
);
我更喜欢将 React 组件与展示组件和容器组件分开。这种方法在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
等其他操作没有什么特别之处,它们的实现方式相同。
【讨论】:
非常感谢您的帮助。仍然试图掌握这个概念。您如何以及在何处调用组件中的操作?您能否进一步说明.then(response => response.json().then(body => ( response, body ))) .then(( response, body ) =>
在做什么?再次感谢
抱歉,body
、response.ok
等被任意命名?或者这就是它们在 API 中的命名方式?最后,我正在尝试使用以下 API "http://www.omdbapi.com?s=star&y=&r=json&plot=short"
和 response
进行测试,想要访问诸如 movie.title
之类的东西。你能举个例子吗?真的很有帮助!
body
和 response.ok
是变量,它们与任何 API 无关。所有必要的响应数据都将在 body
变量中,而不是在 response
电影数组中,可在 body.Search
获得。
非常感谢!接受了答案。你碰巧知道 React Native 吗?
@JoKo,是的,我有一些经验。【参考方案2】:
简短的回答是:
-
redux 不是架构
您可以使用任何库。现在很多人直接使用 fetch API。
为了能够将 redux 与异步操作(AJAX 需要)集成,您需要使用库来提供帮助。正如其他人所说,最受欢迎的两个是
redux-thunk
和 redux-saga
。
对于可以放入您的 redux 应用程序的脑死简单库,您可以尝试redux-crud-store。免责声明:我写的。如果您有兴趣将 fetch API 或其他 API 客户端与 redux-saga 集成,您还可以阅读 redux-crud-store 的源代码
【讨论】:
【参考方案3】:这是 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,这确实是您问题答案的权威来源。
【讨论】:
Thunk 到底做了什么这么特别?似乎您可以在没有任何库的情况下简单地对 API 的 URL 执行 fetch()。redux-thunk
在架构上可用于将异步行为集成到同步的 redux 中。 fetch
足以进行网络调用,但这是最简单的部分。当您开始询问如何从 redux 应用程序进行调用时,您需要像 redux-thunk
这样的东西来将该行为合并到您的 redux 架构中。
非常感谢您的澄清! fetch 是不是 GET 正确?那么 POST、PUT 和 DELETE 会是什么?
fetch
不只是为了获取。你可以用它提出所有类型的请求。 MDN 上的文档很有用,这里有一篇博文,里面有一些例子:davidwalsh.name/fetch以上是关于如何正确地从 ReactJS + Redux 应用程序进行 REST 调用?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Reactjs Redux 应用程序中显示/隐藏 reactstrap 导航栏?
Reactjs Redux 我们应该为状态树中的每个对象创建 sub reducer 吗?