Skip to content

Commit 662eb26

Browse files
authored
Merge pull request #66 from Lemoncode/#61-suspense-samples
#61 suspense samples
2 parents b502af5 + 0a256c9 commit 662eb26

File tree

21 files changed

+672
-0
lines changed

21 files changed

+672
-0
lines changed

examples/06-suspense-like/.babelrc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"useBuiltIns": "usage"
7+
}
8+
],
9+
"@babel/preset-typescript",
10+
"@babel/preset-react"
11+
],
12+
"plugins": [
13+
"@babel/proposal-class-properties",
14+
"@babel/proposal-object-rest-spread"
15+
]
16+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "06-suspense-like",
3+
"version": "1.0.0",
4+
"description": "React Promise Tracker sample with suspense-like component",
5+
"keywords": [
6+
"react",
7+
"promise",
8+
"tracker",
9+
"hook",
10+
"hooks",
11+
"typescript"
12+
],
13+
"author": "Javier Calzado ([email protected])",
14+
"license": "MIT",
15+
"main": "src/index.tsx",
16+
"scripts": {
17+
"start": "webpack-dev-server --mode development --inline --hot --open",
18+
"typecheck": "tsc --pretty --noEmit",
19+
"build": "npm run typecheck && webpack --mode development"
20+
},
21+
"dependencies": {
22+
"@babel/polyfill": "^7.2.5",
23+
"@material-ui/core": "3.9.3",
24+
"react": "16.8.4",
25+
"react-dom": "16.8.4",
26+
"react-promise-tracker": "2.0.2"
27+
},
28+
"devDependencies": {
29+
"@babel/cli": "^7.2.3",
30+
"@babel/core": "^7.2.2",
31+
"@babel/plugin-proposal-class-properties": "^7.1.0",
32+
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
33+
"@babel/preset-env": "^7.3.1",
34+
"@babel/preset-react": "^7.0.0",
35+
"@babel/preset-typescript": "^7.3.3",
36+
"@types/node": "11.13.4",
37+
"@types/react": "16.8.8",
38+
"@types/react-dom": "16.8.2",
39+
"babel-loader": "^8.0.6",
40+
"core-js": "^2.6.5",
41+
"html-webpack-plugin": "^3.2.0",
42+
"tslint": "^5.16.0",
43+
"tslint-react": "^4.0.0",
44+
"typescript": "^3.4.5",
45+
"webpack": "^4.29.3",
46+
"webpack-cli": "^3.2.3",
47+
"webpack-dev-server": "^3.1.14"
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const fetchWithDelay = (url: string, delay: number): Promise<Response> =>
2+
new Promise(resolve => setTimeout(() => resolve(fetch(url, { method: "GET" })), delay));
3+
4+
export interface Quote {
5+
body: string;
6+
author: string;
7+
}
8+
9+
export const getQuote = () =>
10+
fetchWithDelay("https://favqs.com/api/qotd", Math.random() * 3000)
11+
.then(result => result.json())
12+
.then<{ quote: Quote }>(result => result.quote);
13+
14+
const arrayBufferToBase64 = buffer => {
15+
let binary = "";
16+
const bytes = [].slice.call(new Uint8Array(buffer));
17+
bytes.forEach(b => (binary += String.fromCharCode(b)));
18+
return window.btoa(binary);
19+
};
20+
21+
export const getPicture = (width: number, height: number) =>
22+
fetchWithDelay(`https://picsum.photos/${width}/${height}`, Math.random() * 3000).then(res =>
23+
res.arrayBuffer().then(buffer => "data:image/jpeg;base64," + arrayBufferToBase64(buffer))
24+
);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import * as React from "react";
2+
import CircularProgress from "@material-ui/core/CircularProgress";
3+
import Typography from "@material-ui/core/Typography";
4+
import Button from "@material-ui/core/Button";
5+
import { WithStyles, withStyles } from "@material-ui/core/styles";
6+
import { trackPromise, usePromiseTracker } from "react-promise-tracker";
7+
import { Quote, getQuote, getPicture } from "./api";
8+
import { styles } from "./styles";
9+
10+
11+
const Suspense: React.FC<{ tag: string, className?: string }> = ({ tag, className, children }) => {
12+
const { promiseInProgress } = usePromiseTracker({ area: tag });
13+
return promiseInProgress ? <CircularProgress className={className} size={60}/> : <>{children}</>;
14+
};
15+
16+
const AppInner: React.FC<WithStyles<typeof styles>> = ({classes}) => {
17+
const [quote, setQuote] = React.useState<Quote>({ body: "", author: "" });
18+
const [picture, setPicture] = React.useState();
19+
const loadData = React.useCallback(() => {
20+
trackPromise(getQuote(), "quote").then(setQuote);
21+
trackPromise(getPicture(500, 200), "picture").then(setPicture);
22+
}, []);
23+
24+
React.useEffect(() => loadData(), []);
25+
26+
return (
27+
<div className={classes.container}>
28+
<Button variant="outlined" onClick={loadData} className={classes.button}>Refresh</Button>
29+
<Suspense tag="picture" className={classes.progress}>
30+
<img src={picture} className={classes.pic}/>
31+
</Suspense>
32+
<Suspense tag="quote" className={classes.progress}>
33+
<Typography variant="h4" align="center" className={classes.text}>{quote.body}</Typography>
34+
<Typography variant="h5" className={classes.text}>{quote.author}</Typography>
35+
</Suspense>
36+
</div>
37+
);
38+
};
39+
40+
export const App = withStyles(styles)(AppInner);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7+
<title>React Promise Tracker - Suspense</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
</body>
13+
14+
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as React from "react";
2+
import * as ReactDOM from "react-dom";
3+
import CssBaseline from "@material-ui/core/CssBaseline";
4+
import { App } from "./app";
5+
6+
ReactDOM.render(
7+
<>
8+
<CssBaseline />
9+
<App />
10+
</>,
11+
document.getElementById("root")
12+
);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { createStyles } from "@material-ui/core/styles";
2+
3+
export const styles = () => createStyles({
4+
progress: {
5+
margin: "1rem",
6+
},
7+
container: {
8+
display: "flex",
9+
flexDirection: "column",
10+
alignItems: "center",
11+
padding: "2rem",
12+
},
13+
button: {
14+
marginBottom: "2rem",
15+
},
16+
pic: {
17+
marginBottom: "1.25rem",
18+
borderRadius: "8px",
19+
},
20+
text: {
21+
marginBottom: "0.75rem",
22+
}
23+
});
24+
25+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es6",
4+
"module": "es6",
5+
"moduleResolution": "node",
6+
"declaration": false,
7+
"noImplicitAny": false,
8+
"jsx": "react",
9+
"sourceMap": true,
10+
"noLib": false,
11+
"suppressImplicitAnyIndexErrors": true,
12+
"allowSyntheticDefaultImports": true,
13+
},
14+
"compileOnSave": false,
15+
"exclude": ["node_modules"]
16+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
3+
"defaultSeverity": "warning",
4+
"rules": {
5+
"align": [true, "parameters", "statements"],
6+
"array-type": false,
7+
"arrow-parens": false,
8+
"class-name": true,
9+
"comment-format": [true, "check-space"],
10+
"curly": false,
11+
"eofline": true,
12+
"forin": false,
13+
"import-spacing": true,
14+
"indent": [true, "spaces"],
15+
"interface-name": [true, "never-prefix"],
16+
"jsdoc-format": true,
17+
"jsx-no-lambda": false,
18+
"jsx-no-multiline-js": false,
19+
"label-position": true,
20+
"max-line-length": [true, 120],
21+
"member-ordering": false,
22+
"member-access": false,
23+
"no-any": false,
24+
"no-arg": true,
25+
"no-bitwise": true,
26+
"no-console": false,
27+
"no-consecutive-blank-lines": [true, 2],
28+
"no-construct": true,
29+
"no-debugger": true,
30+
"no-default-export": false,
31+
"no-duplicate-variable": true,
32+
"no-empty": true,
33+
"no-empty-interface": false,
34+
"no-eval": true,
35+
"no-implicit-dependencies": false,
36+
"no-internal-module": true,
37+
"no-object-literal-type-assertion": false,
38+
"no-shadowed-variable": true,
39+
"no-string-literal": false,
40+
"no-submodule-imports": false,
41+
"no-trailing-whitespace": true,
42+
"no-unsafe-finally": true,
43+
"no-unused-expression": true,
44+
"no-var-keyword": true,
45+
"no-var-requires": false,
46+
"object-literal-key-quotes": [true, "as-needed"],
47+
"object-literal-sort-keys": false,
48+
"one-line": [true, "check-catch", "check-else", "check-open-brace", "check-whitespace"],
49+
"only-arrow-functions": false,
50+
"ordered-imports": false,
51+
"quotemark": [true, "double", "jsx-double"],
52+
"radix": false,
53+
"semicolon": [true, "always", "strict-bound-class-methods"],
54+
"trailing-comma": [
55+
true,
56+
{
57+
"multiline": {
58+
"objects": "always",
59+
"arrays": "always",
60+
"imports": "always",
61+
"exports": "always",
62+
"typeLiterals": "always"
63+
},
64+
"singleline": "never",
65+
"esSpecCompliant": true
66+
}
67+
],
68+
"triple-equals": [true, "allow-null-check"],
69+
"typedef": [true, "parameter", "property-declaration"],
70+
"typedef-whitespace": [
71+
true,
72+
{
73+
"call-signature": "nospace",
74+
"index-signature": "nospace",
75+
"parameter": "nospace",
76+
"property-declaration": "nospace",
77+
"variable-declaration": "nospace"
78+
}
79+
],
80+
"variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
81+
"whitespace": [
82+
true,
83+
"check-branch",
84+
"check-decl",
85+
"check-module",
86+
"check-operator",
87+
"check-separator",
88+
"check-type",
89+
"check-typecast"
90+
]
91+
}
92+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const HtmlWebpackPlugin = require("html-webpack-plugin");
2+
const path = require("path");
3+
4+
const basePath = __dirname;
5+
6+
module.exports = {
7+
context: path.join(basePath, "src"),
8+
resolve: {
9+
extensions: [".js", ".ts", ".tsx"],
10+
},
11+
entry: ["./index.tsx"],
12+
output: {
13+
path: path.join(basePath, "dist"),
14+
filename: "bundle.js"
15+
},
16+
devtool: "source-map",
17+
devServer: {
18+
contentBase: "./dist", // Content base
19+
inline: true, // Enable watch and live reload
20+
host: "localhost",
21+
port: 8080,
22+
stats: "errors-only"
23+
},
24+
module: {
25+
rules: [
26+
{
27+
test: /\.(tsx?)|(js)$/,
28+
exclude: /node_modules/,
29+
loader: "babel-loader",
30+
},
31+
]
32+
},
33+
plugins: [
34+
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
35+
new HtmlWebpackPlugin({
36+
filename: "index.html", //Name of file in ./dist/
37+
template: "index.html", //Name of template in ./src
38+
hash: true
39+
})
40+
]
41+
};

0 commit comments

Comments
 (0)