It rewritten what I made an article on Nihon Qiita technology sharing service to English.
The article of Japan is as follows.
It is poem. Let's take notice of browsing :)
First of all apologize because you may think you do not understand Atomic Design in the first place.
Just as a common recognition, I also write it in the document, so I will use this method to operate.
Directory structure
- src/
- actions/
- hoge.ts
- api/
- client.ts
- components/
- model/
- type.ts
- store/
- index.ts
- reducer-type.ts
- sagas/
- reducers/
ActionCreator
is described inactions
.
The API surroundings (communication using axios) are summarized in client.ts
.
The components
subordinate is modeled on Atomic Design.
model/type.ts
is managed by separating them with namespace Payload
Response
Domain
.
For example, if you use API to login with email and password Payload.Signin
like
interface Signin {
email: string;
password: string;
}
This is a type to be used as Payload of Action
later.
The response of that API is summarized in Response.Signin
.
example
interface Signin {
name: string;
icon: string;
token: string;
}
store
in addition to index.ts
doing combineReducer
etc, there is reducers
which defines Reducer, sagas
which writes saga, and so on.
It is reducer-type.ts
that helps the shape that I created in this project.
What is contents of reducer-type.ts
reducer-type.ts
has the following contents.
export interface ReduxAPIError {
statusCode: string;
message?: string;
}
interface ReduxAPIStruct<T> {
isLoading: boolean;
status: "success" | "failure";
data: T;
error: ReduxAPIError;
}
export const defaultSet = <T>(defaultValue?): ReduxAPIStruct<T> => ({
isLoading: false,
status: null,
data: defaultValue || null,
error: errorDefault()
});
export const errorDefault = (): ReduxAPIError => ({
statusCode: null,
message: ""
});
export default ReduxAPIStruct;
Inside the application I read this useful tool as ReduxAPIStruct
.
What is important is the interface below.
interface ReduxAPIStruct<T> {
isLoading: boolean;
status: "success" | "failure";
data: T;
error: ReduxAPIError;
}
Each describes the motion when communicating with the application.
isLoading
is managed by boolean and deals with whether or not it is loading.
status
is created to facilitate fallback to the error component later.
data
is the data body when actually obtained from the API. Here is defined as generic with T
. When using this type later, pass the Response
type of model/type.ts
just as it is.
error
has a type called ReduxAPIError
.
export interface ReduxAPIError {
statusCode: string;
message?: string;
}
ReduxAPIError
has statusCode
and message
.
statusCode
sets the statusCode of the response when accessing that API.
Message has a type to insert it in the response body from the server and later in saga.
A world with ReduxAPIStruct
First of all, as an important point of what imitated Atomic Design, we separated the clear responsibilities of pages
and template
.
For example, connect redux only to components of paegs
class.
We define pages
as a single page which has data and is linked to the URL .
On the other hand, template
was created as a UI based on data .
By separating this clear obligation, the pages
component looks like this.
For example, I would like to create an article list page that receives the following data.
{
"items": [
{
"title": "タイトル1",
"body": "本文1"
},
{
"title": "タイトル2",
"body": "本文2"
},
{
"title": "タイトル3",
"body": "本文3"
}
]
}
Suppose that the endpoint of this API is [GET] /items
and the axios API client is an implementation as follows.
import axios from "axios";
class Client {
public getItems() {
return axios.get("/items");
}
}
The client implements it to return Promise
.
The saga code will end, but let's say the result is in the key items
of article reducer
.
import * as React from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import { requestGetItems } from "~/actions/article";
import ErrorHandler from "~/ErrorHandler";
// templates
import ItemsPageTemplate from "~/components/templates/ItemsPage";
import LoadingTemplate from "~/components/templates/Loading";
// type
import { Response } from "~/model/type";
import ReduxAPIStruct from "~/store/reducer-type";
interface Props {
items: ReduxAPIStruct<Response.Article.Items>;
}
class ItemPage extends React.Component {
public render() {
const { items } = this.props;
if(items.status === "failure") {
return <ErrorHandler errorStatus={items.error.statusCode}/>;
}
return items.isLoading ? <LoadingTemplate /> : <ItemsPageTemplate items={items.data} />;
}
}
export default wituRouter(connect(
state => ({ items: state.articleReducer.items })
)(ItemPage) as any);
If data is being acquired with this, LoadingTemplate
will fail and fetch of data will fail.
If it fails to ErrorHandler
it succeeds and ItemsPage
will be mounted.
You can manage these three states regardless of which page you write or not thinking about anything.
Thanks to ReduxAPIStruct you can easily understand whether that data is in fetch or not, and failing fetch can also be understood by looking at status
.
When saga is written by cutting this state management to another state, it is possible to manage the state without thinking about the component.
Afterword
I translate my English using Google Translate. If there is a mistake as a description of English, please point out with comments :(
Twitter: @konojunya
GitHub: konojunya
Top comments (0)