Skip to content

Commit 31ec1c2

Browse files
committed
11 Table axios migrated
1 parent e59783d commit 31ec1c2

19 files changed

Lines changed: 584 additions & 0 deletions

hooks/11_TableAxios/.babelrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"useBuiltIns": "entry"
7+
}
8+
]
9+
]
10+
}

hooks/11_TableAxios/Readme.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# 11 Table fetch
2+
3+
In this sample we are going to update the previous sampe (mock table) and instead of
4+
returning mock data, return real data from the github rest api.
5+
6+
# Steps to reproduce the sample
7+
8+
- We will take as starting point sample _10 tableMock_, let's copy the content from this
9+
sample and execute _npm install_.
10+
11+
```bash
12+
npm install
13+
```
14+
15+
- To retrieve data from the Github REST api we will make use of axios, let's install the package
16+
(no need to install typing, they are already included in the library).
17+
18+
```bash
19+
npm install axios --save
20+
```
21+
22+
- Time to open the _memberApi_ file and replace the mock data with a real api call.
23+
24+
_./src/api/memberApi.ts_
25+
26+
```diff
27+
import { MemberEntity } from "../model/member";
28+
+ import Axios, { AxiosResponse } from 'axios';
29+
30+
+ const gitHubURL = 'https://api.github.com';
31+
+ const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`;
32+
33+
export const getMembersCollection = (): Promise<MemberEntity[]> => {
34+
const promise = new Promise<MemberEntity[]>((resolve, reject) => {
35+
+ try {
36+
+ Axios.get<MemberEntity[]>(gitHubMembersUrl)
37+
+ .then(response => resolve(mapMemberListApiToModel(response)));
38+
+ } catch (ex) {
39+
+ reject(ex);
40+
+ }
41+
- setTimeout(
42+
- () =>
43+
- resolve([
44+
- {
45+
- id: 1457912,
46+
- login: "brauliodiez",
47+
- avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3"
48+
- },
49+
- {
50+
- id: 4374977,
51+
- login: "Nasdan",
52+
- avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3"
53+
- }
54+
- ]),
55+
- 500
56+
- );
57+
});
58+
59+
return promise;
60+
};
61+
62+
+ const mapMemberListApiToModel = ({data}: AxiosResponse<any[]>): MemberEntity[] =>
63+
+ data.map(gitHubMember => ({
64+
+ id: gitHubMember.id,
65+
+ login: gitHubMember.login,
66+
+ avatar_url: gitHubMember.avatar_url
67+
+ }));
68+
```
69+
70+
- Aaaand... we don't need to add any update on the rest of the application, why?
71+
The function is providing the same contract it returns a promise<MemberEntity[]>,
72+
let's give a try:
73+
74+
```bash
75+
npm start
76+
```

hooks/11_TableAxios/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "react-typescript-by-sample",
3+
"version": "1.0.0",
4+
"description": "React Typescript examples",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "webpack-dev-server --mode development --inline --hot --open",
8+
"build": "webpack --mode development"
9+
},
10+
"keywords": [
11+
"react",
12+
"typescript",
13+
"hooks"
14+
],
15+
"author": "Braulio Diez Botella",
16+
"license": "MIT",
17+
"devDependencies": {
18+
"@babel/cli": "^7.2.3",
19+
"@babel/core": "^7.2.2",
20+
"@babel/polyfill": "^7.2.5",
21+
"@babel/preset-env": "^7.3.1",
22+
"@types/node": "^11.9.4",
23+
"@types/react": "^16.8.3",
24+
"@types/react-dom": "^16.8.1",
25+
"awesome-typescript-loader": "^5.2.1",
26+
"babel-loader": "^8.0.5",
27+
"css-loader": "^2.1.0",
28+
"file-loader": "^3.0.1",
29+
"html-webpack-plugin": "^3.2.0",
30+
"mini-css-extract-plugin": "^0.5.0",
31+
"style-loader": "^0.23.1",
32+
"typescript": "^3.3.3",
33+
"url-loader": "^1.1.2",
34+
"webpack": "^4.29.3",
35+
"webpack-cli": "^3.2.3",
36+
"webpack-dev-server": "^3.1.14"
37+
},
38+
"dependencies": {
39+
"axios": "^0.18.0",
40+
"react": "^16.8.2",
41+
"react-dom": "^16.8.2"
42+
}
43+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { MemberEntity } from "../model/member";
2+
import Axios, { AxiosResponse } from "axios";
3+
4+
const gitHubURL = "https://api.github.com";
5+
const gitHubMembersUrl = `${gitHubURL}/orgs/lemoncode/members`;
6+
7+
export const getMembersCollection = (): Promise<MemberEntity[]> => {
8+
const promise = new Promise<MemberEntity[]>((resolve, reject) => {
9+
try {
10+
Axios.get<MemberEntity[]>(gitHubMembersUrl).then(response =>
11+
resolve(mapMemberListApiToModel(response))
12+
);
13+
} catch (ex) {
14+
reject(ex);
15+
}
16+
});
17+
18+
return promise;
19+
};
20+
21+
const mapMemberListApiToModel = ({
22+
data
23+
}: AxiosResponse<any[]>): MemberEntity[] =>
24+
data.map(gitHubMember => ({
25+
id: gitHubMember.id,
26+
login: gitHubMember.login,
27+
avatar_url: gitHubMember.avatar_url
28+
}));

hooks/11_TableAxios/src/app.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import * as React from "react";
2+
import {
3+
HelloComponent,
4+
NameEditComponent,
5+
ColorBrowser,
6+
ColorPicker,
7+
SidebarComponent,
8+
MemberTableComponent
9+
} from "./components";
10+
import { Color } from "./model/color";
11+
12+
export const App = () => {
13+
const [name, setName] = React.useState("defaultUserName");
14+
const [editingName, setEditingName] = React.useState("defaultUserName");
15+
const [color, setColor] = React.useState<Color>({
16+
red: 20,
17+
green: 40,
18+
blue: 180
19+
});
20+
const [isVisible, setVisible] = React.useState(false);
21+
22+
const loadUsername = () => {
23+
setTimeout(() => {
24+
setName("name from async call");
25+
setEditingName("name from async call");
26+
}, 500);
27+
};
28+
29+
React.useEffect(() => {
30+
loadUsername();
31+
}, []);
32+
33+
const setUsernameState = () => {
34+
setName(editingName);
35+
};
36+
37+
return (
38+
<>
39+
<SidebarComponent isVisible={isVisible}>
40+
<h1>Cool Scfi movies</h1>
41+
<ul>
42+
<li>
43+
<a href="https://www.imdb.com/title/tt0816692/">Interstellar</a>
44+
</li>
45+
<li>
46+
<a href="https://www.imdb.com/title/tt0083658/">Blade Runner</a>
47+
</li>
48+
<li>
49+
<a href="https://www.imdb.com/title/tt0062622/">
50+
2001: a space odyssey
51+
</a>
52+
</li>
53+
</ul>
54+
</SidebarComponent>
55+
<MemberTableComponent />
56+
<ColorBrowser color={color} />
57+
<ColorPicker color={color} onColorUpdated={setColor} />
58+
<HelloComponent userName={name} />
59+
<NameEditComponent
60+
initialUserName={name}
61+
editingName={editingName}
62+
onNameUpdated={setUsernameState}
63+
onEditingNameUpdated={setEditingName}
64+
disabled={editingName === "" || editingName === name}
65+
/>
66+
<div style={{ float: "right" }}>
67+
<button onClick={() => setVisible(!isVisible)}>Toggle Sidebar</button>
68+
</div>
69+
</>
70+
);
71+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as React from "react";
2+
import { Color } from "../model/color";
3+
4+
interface Props {
5+
color: Color;
6+
}
7+
8+
export const ColorBrowser = (props: Props) => {
9+
const divStyle: React.CSSProperties = {
10+
width: "11rem",
11+
height: "7rem",
12+
backgroundColor: `rgb(${props.color.red},${props.color.green}, ${
13+
props.color.blue
14+
})`
15+
};
16+
17+
return <div style={divStyle} />;
18+
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as React from "react";
2+
import { Color } from "../model/color";
3+
4+
interface Props {
5+
color: Color;
6+
onColorUpdated: (color: Color) => void;
7+
}
8+
9+
const updateColor = (props: Props, colorId: keyof Color) => value => {
10+
// keyof Color ensures only 'red', 'blue' or 'green' can be passed in.
11+
props.onColorUpdated({
12+
...props.color, // this creates a clone of the current props.color object...
13+
[colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value.
14+
});
15+
};
16+
17+
export const ColorPicker = (props: Props) => (
18+
<div>
19+
<ColorSliderComponent
20+
value={props.color.red}
21+
onValueUpdated={updateColor(props, "red")}
22+
/>
23+
<br />
24+
<ColorSliderComponent
25+
value={props.color.green}
26+
onValueUpdated={updateColor(props, "green")}
27+
/>
28+
<br />
29+
<ColorSliderComponent
30+
value={props.color.blue}
31+
onValueUpdated={updateColor(props, "blue")}
32+
/>
33+
{props.color.blue}
34+
<br />
35+
</div>
36+
);
37+
38+
interface PropsColorSlider {
39+
value: number;
40+
onValueUpdated: (newValue: number) => void;
41+
}
42+
43+
const ColorSliderComponent = (props: PropsColorSlider) => {
44+
return (
45+
<div>
46+
<input
47+
type="range"
48+
min="0"
49+
max="255"
50+
value={props.value}
51+
onChange={event => props.onValueUpdated(+event.target.value)}
52+
/>
53+
{props.value}
54+
</div>
55+
);
56+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as React from "react";
2+
3+
interface Props {
4+
userName: string;
5+
}
6+
7+
export const HelloComponent = (props: Props) => {
8+
return <h2>Hello user: {props.userName} !</h2>;
9+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export * from "./hello";
2+
export * from "./nameEdit";
3+
export * from "./colorBrowser";
4+
export * from "./colorPicker";
5+
export * from "./sidebar";
6+
export * from "./memberTable";
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as React from "react";
2+
import { MemberEntity } from "../model/member";
3+
import { getMembersCollection } from "../api/memberApi";
4+
5+
const useMemberCollection = () => {
6+
const [memberCollection, setMemberCollection] = React.useState<
7+
MemberEntity[]
8+
>([]);
9+
10+
const loadMemberCollection = () => {
11+
getMembersCollection().then(memberCollection =>
12+
setMemberCollection(memberCollection)
13+
);
14+
};
15+
16+
return { memberCollection, loadMemberCollection };
17+
};
18+
19+
export const MemberTableComponent = () => {
20+
const { memberCollection, loadMemberCollection } = useMemberCollection();
21+
22+
React.useEffect(() => {
23+
loadMemberCollection();
24+
}, []);
25+
26+
return (
27+
<>
28+
<table>
29+
<thead>
30+
<tr>
31+
<th>Avatar</th>
32+
<th>Id</th>
33+
<th>Name</th>
34+
</tr>
35+
</thead>
36+
<tbody>
37+
{memberCollection.map(member => (
38+
<MemberRow key={member.id} member={member} />
39+
))}
40+
</tbody>
41+
</table>
42+
</>
43+
);
44+
};
45+
46+
const MemberRow = ({ member }: { member: MemberEntity }) => (
47+
<tr>
48+
<td>
49+
<img src={member.avatar_url} style={{ maxWidth: "10rem" }} />
50+
</td>
51+
<td>
52+
<span>{member.id}</span>
53+
</td>
54+
<td>
55+
<span>{member.login}</span>
56+
</td>
57+
</tr>
58+
);

0 commit comments

Comments
 (0)