Handle RESTful Requests Data in React/Redux Apps
在React / Redux应用程序中处理RESTful请求数据
在React / Redux应用程序中处理RESTful请求数据 (Handle RESTful Requests Data in React/Redux Apps)
The idea is to handle modeling, fetching, and displaying RESTful requests data (Remote data) in React/Redux apps.
这个想法是在React / Redux应用程序中处理建模,获取和显示RESTful请求数据(远程数据)。
As a web developer, I have to deal with remote sources (e.g., HTTP) in my apps. During working with react, I was following an ordinary behavior by making the HTTP requests and then store the fetched data in the application store (e.g., redux store).
作为一名Web开发人员,我必须在我的应用程序中处理远程源(例如HTTP)。 在使用react的过程中,我通过发出HTTP请求来遵循通常的行为,然后将获取的数据存储在应用程序存储区(例如redux存储区)中。
It was a simple data type that maps the API response data. For example, if I fetch a list of users, each user has some properties (id, username, gender, etc…). The datatype that would represent that response is an array of User
where User
is the data type representing the user details.
这是映射API响应数据的简单数据类型。 例如,如果我获取用户列表,则每个用户都有一些属性(id,用户名,性别等)。 表示该响应的数据类型是一个User
数组,其中User
是表示用户详细信息的数据类型。
type User {
id: string,
username: string,
gender: 'Male' | 'Female'
.
.
.
}State = {
users: User[]
}
Suppose we want to display a loading placeholder while data is fetching and an empty view if the list of users is empty. The previous implementation (representation of data) doesn’t have much data that we can use to do so. We can add a loading flag to the State. Then in the UI, we check this flag before displaying the data.
假设我们要在获取数据时显示一个加载占位符,如果用户列表为空,则显示一个空视图。 以前的实现(数据表示)没有太多数据可用于实现此目的。 我们可以在状态中添加一个加载标志。 然后在用户界面中,我们在显示数据之前检查此标志。
type User {
id: string,
username: string,
gender: 'Male' | 'Female'
.
.
.
}State = {
loading: true,
users: User[]
}
There is no guarantee that you have to check this flag before displaying the list of users. If someone forgot, In slow connectivity, The empty placeholder may appear for seconds while data is loading. The previous implementation does not represent remote data well. So we need to find an explicit data model to represent RESTful requests data well in our application.
无法保证您必须在显示用户列表之前检查此标志。 如果有人忘记了,在慢速连接中,在加载数据时,空白占位符可能会显示几秒钟。 先前的实现不能很好地表示远程数据。 因此,我们需要找到一个明确的数据模型来很好地表示我们应用程序中的RESTful请求数据。
The following section inspired by RemoteData
in elm.
下面的部分灵感 通过 RemoteData
在榆树 。
RemoteData类型 (RemoteData type)
RESTful requests have one of four states:
RESTful请求具有以下四种状态之一:
- We haven’t asked for the data yet. 我们尚未要求提供数据。
- We’ve asked, but we haven’t got a response yet. 我们已经问过,但还没有得到答复。
- We got a response, but there was an error. 我们得到了回应,但有一个错误。
- We got a response, and here’s the data you asked. 我们得到了回应,这是您要求的数据。
First, we will wrap our remote data type in a new model called RemoteData
首先,我们将远程数据类型包装在一个名为RemoteData
的新模型中
Defining RemoteData
type
定义 RemoteData
类型
RemoteData<T, E>
whereT
is the data type andE
is the error type respectively.
RemoteData<T, E>
其中T
是数据类型,E
是错误类型。
So our state representation of users list example explained above will beHere we assume that our error type is a string
因此,我们上面解释的用户列表示例的状态表示为: 这里我们假定我们的错误类型是字符串
State = {
users: RemoteData<User[], string>
}
When we fetch a list of users
from an API endpoint, there are four different states users
can be:
当我们从API端点获取users
列表时, users
可以有四种不同的状态:
NOT_ASKED
- You didn’t ask for the data yetNOT_ASKED
您尚未请求数据LOADING
- You asked for the data but it’s not fetched yet.LOADING
-您要求提供数据,但尚未提取。REJECT
- Something went wrong. Here's the error.REJECT
-发生错误。 这是错误。SUCCESS
- Data fetched successfully. Here’s the requested data.SUCCESS
-数据获取成功。 这是要求的数据。
And that’s it, You have now a better representation of remote data that will lead to a better UI.
就是这样,您现在可以更好地表示远程数据,从而带来更好的UI。
API请求包装器 (API request wrapper)
Since we now have a good representation of remote data, It’s time to handle the fetching of it and updating the application store. When calling an API endpoint to fetch data, we’ll need to update the application store, and since updating the application store in redux requires dispatching an action to tell reducer to update the store, I’ve created a helper function called api
request wrapper based on Axios
to do the job of making the HTTP requests and dispatching the reasonable action in each state of the HTTP request (loading, reject, and success).
由于我们现在可以很好地表示远程数据,因此现在该处理获取数据并更新应用程序存储的时候了。 调用API端点以获取数据时,我们需要更新应用程序商店,并且由于在redux中更新应用程序商店需要调度一个操作来告诉reducer更新商店,因此我创建了一个名为api
request wrapper的辅助函数。基于Axios
的工作来发出HTTP请求并在HTTP请求的每种状态(加载,拒绝和成功)中调度合理的操作。
Defining api
request wrapper
定义 api
请求包装器
api<T, E>
(config) whereT
,E
are the types of data and the expected error respectively
api<T, E>
(config)其中T
,E
分别是数据类型和预期错误
api<User[], ErrorResponse>({
method: 'GET',
url: 'users',
baseURL: 'https://jsonplaceholder.typicode.com/',
action: FETCH_USERS,
});
Request Config
请求配置
In addition to Axios request config there are three more options:
除了Axios 请求配置外,还有三个选项:
action
: is the action type that will get dispatched when the request state changed. If it’s not provided no action will get dispatched.action
:是请求状态更改时将分派的操作类型。 如果未提供,则不会分派任何操作。onSuccess
,onError
: are the callbacks to be triggered for the relevant request state.onSuccess
,onError
:是为相关请求状态触发的回调。
The possible actions that can get dispatched from api
request wrapper are:
可以从api
请求包装程序中分派的可能动作是:
LOADING
- You asked for the data but it’s not fetched yet.LOADING
-您要求提供数据,但尚未提取。REJECT
- Something went wrong. Here's the error.REJECT
-发生错误。 这是错误。SUCCESS
- Data fetched successfully. Here’s the requested data.SUCCESS
-数据获取成功。 这是要求的数据。
The attribute that differentiates between loading, reject, and success of an action is the kind
attribute.
区分加载,拒绝和动作成功的属性是kind
属性。
Here’s some example of loading, reject, and success of fetch users list action.
这是加载,拒绝和成功获取用户列表操作的一些示例。
// Loading
{
action: 'FETCH_USERS',
kind: RemoteKind.Loading,
}// Reject
{
action: 'FETCH_USERS',
kind: RemoteKind.Reject,
error: 'Something went wrong'
}// Success
{
action: 'FETCH_USERS',
kind: RemoteKind.Success,
data: [{...}, {...}]
}
Defining Action
type
定义 Action
类型
Action<T, E>
whereT
is the data type andE
is the error type respectively
Action<T, E>
其中T
是数据类型,E
是错误类型
Instead of handling those actions by yourself for each HTTP request, api
request wrapper will do that for you.
api
请求包装器将代替您为每个HTTP请求自己处理这些操作。
Suppose you want to create an action for fetching users. Here’s an example that shows how the action file would look like with and without using the api
request wrapper.
假设您要创建一个用于获取用户的操作。 这是一个示例,显示了使用和不使用api
请求包装器时动作文件的外观。
Without using the api
request wrapper, Your action file would be:
不使用 api
请求包装器,您的操作文件将是:
// action.tsconst FETCH_USERS = 'FETCH_USERS'
const FETCH_USERS_FAILED = 'FETCH_USERS_FAILED'
const FETCH_USERS_FULFILLED = 'FETCH_USERS_FULFILLED'const fetchUsers = () => (dispatch) => {
dispatch({type: 'FETCH_USERS'})
axios({
method: 'GET',
url: 'posts',
baseURL: 'https://jsonplaceholder.typicode.com/',
})
.then(res => dispatch({
type: FETCH_USERS_FULFILLED,
payload: res.data
})
.catch(err => dispatch({
type: FETCH_USERS_FAILED,
payload: err
})
}
By using the api
request wrapper, it will be simpler, Just need to pass an extra param action
that will get dispatched when the request state changed.
通过使用 api
请求包装器,它会变得更简单,只需要传递一个额外的param action
,该 action
将在请求状态更改时分派。
kind
is the attribute differentiates between different request states (loading, reject, and success) of an action.
kind
是属性,用于区分动作的不同请求状态(加载,拒绝和成功)。
// actions.tsconst FETCH_USERS = 'FETCH_USERS'
const fetchUsers = () =>
api<User[], ErrorResponse>({
method: 'GET',
url: 'users',
baseURL: 'https://jsonplaceholder.typicode.com/',
action: FETCH_USERS,
});
获取数据缩减器 (Fetching data reducer)
Let’s catch the dispatched actions and update the application store. I’ve created a reducer that can handle the different actions for any remote data and update the store.
让我们捕获调度的动作并更新应用程序商店。 我创建了一个reducer,可以处理任何远程数据的不同操作并更新商店。
Defining fechingReducer
定义 fechingReducer
fetchingReducer<T, E>
(actionType) a reducer for managing the state of the remote data.
fetchingReducer<T, E>
(actionType)用于管理远程数据状态的缩减器。
import { fetchingReducer } from 'remote-data';combineReducers({
users: fetchingReducer<User[], ErrorResponse>(FETCH_USERS),
});
actionType
: it should be the same as the action passed to theapi
request wrapper.actionType
:它应该与传递给api
请求包装程序的操作相同。
创建一个定制的reducer来手动更新商店(可选) (Creating a custom reducer to manually update the store (Optional))
You can create your custom reducer, here’s an example:
您可以创建您的自定义减速器,下面是一个示例:
import { RemoteData, RemoteKind, Action } from 'remote-data';
import { User, ErrorResponse } from '../../models';
import { FETCH_USERS } from './constants';
export type UsersStore = {
users: RemoteData<User[], ErrorResponse>;
};
const initialState: UsersStore = {
users: {
kind: RemoteKind.NotAsked,
},
};
export default (
state: UsersStore = initialState,
action: Action<User[], ErrorResponse>,
): UsersStore => {
if (action.type === FETCH_USERS) {
switch (action.kind) {
case RemoteKind.Loading:
return {
...state,
users: {
kind: RemoteKind.Loading,
},
};
case RemoteKind.Success:
return {
...state,
users: {
kind: RemoteKind.Success,
data: action.data,
},
};
case RemoteKind.Reject:
return {
...state,
users: {
kind: RemoteKind.Reject,
error: action.error,
},
};
default:
return state;
}
}
return state;
};
- Initialize your state 初始化您的状态
- Verify the action type and kind 验证动作类型和种类
- Update your state 更新您的状态
action
is of type Action<T, E>
action
的类型为Action <T,E>
远程组件 (Remote Component)
The last part is to handle the display of remote data in its different states.I’ve created a component that takes your remote data and the views that you want to display in each state Loading
, Success
, and Reject
.
最后一部分是处理处于不同状态的远程数据的显示。我创建了一个组件,该组件接收远程数据以及要在每种状态Loading
, Success
和Reject
显示的视图。
<RemoteComponent
remote={{ users }}
loading={UsersLoading}
reject={({ users }) => <UsersError error={users.error} />}
success={({ users }) => <ListUsers users={users.data} />}
/>
I’ve created a small react library that provides:
api request wrapper based on Axios to make the HTTP requests
fetchingReducer to update the store
fetchingReducer更新商店
RemoteComponent to handle displaying remote data
RemoteComponent处理显示远程数据
安装 (Install)
npm i @alismael/remote-data
用法 (Usage)
Performing a GET
request to fetch the data
执行GET
请求以获取数据
import { api } from 'remote-data';
import { Post, ErrorResponse } from '../models';
import { FETCH_POSTS } from './constants';const fetchPosts = () =>
api<Post[], ErrorResponse>({
method: 'GET',
url: 'posts',
baseURL: 'https://jsonplaceholder.typicode.com/',
action: FETCH_POSTS,
});
Adding a reducer to update the store
添加减速器以更新商店
import { Reducer } from 'react';
import { combineReducers } from 'redux';
import { fetchingReducer, RemoteData } from 'remote-data';
import { Post, ErrorResponse } from '../../models';
import { FETCH_POSTS } from './constants';export type PostsStore = {
posts: RemoteData<Post[], ErrorResponse>;
};const postsReducer: Reducer<PostsStore, any> = combineReducers({
posts: fetchingReducer<Post[], ErrorResponse>(FETCH_POSTS),
});export default postsReducer;
Displaying your remote data
显示您的远程数据
const PostsLoading = () => <>Loading posts...</>;
const PostsError = ({ err }: { err: ErrorResponse }) => <>{err}</>;
const ListPosts = ({ data }: { data: Post[] }) => <>Here you can use the fetched data</>type PostsContainerProps = {
fetchPosts: () => Promise<Post[]>;
posts: RemoteData<Post[], ErrorResponse>;
};const PostsContainer = ({ fetchPosts, posts }: PostsContainerProps) => {
React.useEffect(() => {
fetchPosts();
}, [fetchPosts]);return (
<RemoteComponent
remote={{ posts }}
loading={PostsLoading}
reject={({ posts }) => <PostsError error={posts.error} />}
success={({ posts }) => <ListPosts posts={posts.data} />}
/>
);
};const mapStateToProps = ({ posts }: StoreState) => ({
posts: posts.posts,
});
const mapDispatchToProps = (
dispatch,
) => ({
fetchPosts: () => dispatch(fetchPostsAction()),
});
connect(mapStateToProps, mapDispatchToProps)(PostsContainer);
GitHub: https://github.com/alismael/remote-dataNPM: https://www.npmjs.com/@alismael/remote-data
GitHub: https : //github.com/alismael/remote-data NPM: https : //www.npmjs.com/@alismael/remote-data
And that’s it. Thanks for reading.
就是这样。 谢谢阅读。
翻译自: https://medium.com/swlh/handle-restful-requests-data-in-react-redux-apps-23cee75f3324