diff --git a/hooks/00_BoilerPlate/package.json b/hooks/00_BoilerPlate/package.json
index a259e937..12b2f5b2 100644
--- a/hooks/00_BoilerPlate/package.json
+++ b/hooks/00_BoilerPlate/package.json
@@ -15,21 +15,21 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
}
}
diff --git a/hooks/00_BoilerPlate/webpack.config.js b/hooks/00_BoilerPlate/webpack.config.js
index bf1cad6c..37715722 100644
--- a/hooks/00_BoilerPlate/webpack.config.js
+++ b/hooks/00_BoilerPlate/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./main.ts"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/01_HelloReact/package.json b/hooks/01_HelloReact/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/01_HelloReact/package.json
+++ b/hooks/01_HelloReact/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/01_HelloReact/webpack.config.js b/hooks/01_HelloReact/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/01_HelloReact/webpack.config.js
+++ b/hooks/01_HelloReact/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/02_Properties/package.json b/hooks/02_Properties/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/02_Properties/package.json
+++ b/hooks/02_Properties/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/02_Properties/src/hello.tsx b/hooks/02_Properties/src/hello.tsx
index 38df41c1..b9b1d810 100644
--- a/hooks/02_Properties/src/hello.tsx
+++ b/hooks/02_Properties/src/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => (
-
Hello user: {props.userName} !
-);
\ No newline at end of file
+export const HelloComponent: React.FC = (props) => (
+ Hello user: {props.userName} !
+);
diff --git a/hooks/02_Properties/webpack.config.js b/hooks/02_Properties/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/02_Properties/webpack.config.js
+++ b/hooks/02_Properties/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/03_State/package.json b/hooks/03_State/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/03_State/package.json
+++ b/hooks/03_State/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/03_State/src/hello.tsx b/hooks/03_State/src/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/03_State/src/hello.tsx
+++ b/hooks/03_State/src/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/03_State/src/nameEdit.tsx b/hooks/03_State/src/nameEdit.tsx
index 09c4cd1c..02f7e36d 100644
--- a/hooks/03_State/src/nameEdit.tsx
+++ b/hooks/03_State/src/nameEdit.tsx
@@ -5,7 +5,7 @@ interface Props {
onChange: (e: React.ChangeEvent) => void;
}
-export const NameEditComponent = (props: Props) => (
+export const NameEditComponent: React.FC = (props) => (
<>
Update name:
diff --git a/hooks/03_State/webpack.config.js b/hooks/03_State/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/03_State/webpack.config.js
+++ b/hooks/03_State/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/04_Callback/package.json b/hooks/04_Callback/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/04_Callback/package.json
+++ b/hooks/04_Callback/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/04_Callback/src/hello.tsx b/hooks/04_Callback/src/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/04_Callback/src/hello.tsx
+++ b/hooks/04_Callback/src/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/04_Callback/src/nameEdit.tsx b/hooks/04_Callback/src/nameEdit.tsx
index 53edae95..8b1a5ca8 100644
--- a/hooks/04_Callback/src/nameEdit.tsx
+++ b/hooks/04_Callback/src/nameEdit.tsx
@@ -5,7 +5,7 @@ interface Props {
onNameUpdated: (newName: string) => any;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const [editingName, setEditingName] = React.useState(props.initialUserName);
const onChange = (e: React.ChangeEvent) => {
diff --git a/hooks/04_Callback/webpack.config.js b/hooks/04_Callback/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/04_Callback/webpack.config.js
+++ b/hooks/04_Callback/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/05_Refactor/package.json b/hooks/05_Refactor/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/05_Refactor/package.json
+++ b/hooks/05_Refactor/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/05_Refactor/src/hello.tsx b/hooks/05_Refactor/src/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/05_Refactor/src/hello.tsx
+++ b/hooks/05_Refactor/src/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/05_Refactor/src/nameEdit.tsx b/hooks/05_Refactor/src/nameEdit.tsx
index 57f53dd0..c2eebf31 100644
--- a/hooks/05_Refactor/src/nameEdit.tsx
+++ b/hooks/05_Refactor/src/nameEdit.tsx
@@ -7,7 +7,7 @@ interface Props {
onEditingNameUpdated: (newEditingName: string) => any;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
diff --git a/hooks/05_Refactor/webpack.config.js b/hooks/05_Refactor/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/05_Refactor/webpack.config.js
+++ b/hooks/05_Refactor/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/06_Enable/package.json b/hooks/06_Enable/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/06_Enable/package.json
+++ b/hooks/06_Enable/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/06_Enable/src/hello.tsx b/hooks/06_Enable/src/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/06_Enable/src/hello.tsx
+++ b/hooks/06_Enable/src/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/06_Enable/src/nameEdit.tsx b/hooks/06_Enable/src/nameEdit.tsx
index 646a4d26..7f9091e3 100644
--- a/hooks/06_Enable/src/nameEdit.tsx
+++ b/hooks/06_Enable/src/nameEdit.tsx
@@ -5,10 +5,10 @@ interface Props {
editingName: string;
onNameUpdated: () => any;
onEditingNameUpdated: (newEditingName: string) => any;
- disabled : boolean;
+ disabled: boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
@@ -21,10 +21,9 @@ export const NameEditComponent = (props: Props) => {
<>
Update name:
- Change
+
+ Change
+
>
);
};
diff --git a/hooks/06_Enable/webpack.config.js b/hooks/06_Enable/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/06_Enable/webpack.config.js
+++ b/hooks/06_Enable/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/07_ColorPicker/package.json b/hooks/07_ColorPicker/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/07_ColorPicker/package.json
+++ b/hooks/07_ColorPicker/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/07_ColorPicker/src/components/colorBrowser.tsx b/hooks/07_ColorPicker/src/components/colorBrowser.tsx
index 7dcc3905..813746ce 100644
--- a/hooks/07_ColorPicker/src/components/colorBrowser.tsx
+++ b/hooks/07_ColorPicker/src/components/colorBrowser.tsx
@@ -5,13 +5,11 @@ interface Props {
color: Color;
}
-export const ColorBrowser = (props: Props) => {
+export const ColorBrowser: React.FC = (props) => {
const divStyle: React.CSSProperties = {
width: "11rem",
height: "7rem",
- backgroundColor: `rgb(${props.color.red},${props.color.green}, ${
- props.color.blue
- })`
+ backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})`,
};
return
;
diff --git a/hooks/07_ColorPicker/src/components/colorPicker.tsx b/hooks/07_ColorPicker/src/components/colorPicker.tsx
index 91283505..23bdca3c 100644
--- a/hooks/07_ColorPicker/src/components/colorPicker.tsx
+++ b/hooks/07_ColorPicker/src/components/colorPicker.tsx
@@ -6,7 +6,7 @@ interface Props {
onColorUpdated: (color: Color) => void;
}
-export const ColorPicker = (props: Props) => (
+export const ColorPicker: React.FC = (props) => (
{
+export const HelloComponent: React.FC
= (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/07_ColorPicker/src/components/nameEdit.tsx b/hooks/07_ColorPicker/src/components/nameEdit.tsx
index 646a4d26..5461073e 100644
--- a/hooks/07_ColorPicker/src/components/nameEdit.tsx
+++ b/hooks/07_ColorPicker/src/components/nameEdit.tsx
@@ -8,7 +8,7 @@ interface Props {
disabled : boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
diff --git a/hooks/07_ColorPicker/webpack.config.js b/hooks/07_ColorPicker/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/07_ColorPicker/webpack.config.js
+++ b/hooks/07_ColorPicker/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/08_ColorPickerRefactor/package.json b/hooks/08_ColorPickerRefactor/package.json
index bfa0e4eb..264e71d2 100644
--- a/hooks/08_ColorPickerRefactor/package.json
+++ b/hooks/08_ColorPickerRefactor/package.json
@@ -15,27 +15,27 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/08_ColorPickerRefactor/src/components/colorBrowser.tsx b/hooks/08_ColorPickerRefactor/src/components/colorBrowser.tsx
index 7dcc3905..a28e438e 100644
--- a/hooks/08_ColorPickerRefactor/src/components/colorBrowser.tsx
+++ b/hooks/08_ColorPickerRefactor/src/components/colorBrowser.tsx
@@ -5,7 +5,7 @@ interface Props {
color: Color;
}
-export const ColorBrowser = (props: Props) => {
+export const ColorBrowser: React.FC = (props) => {
const divStyle: React.CSSProperties = {
width: "11rem",
height: "7rem",
diff --git a/hooks/08_ColorPickerRefactor/src/components/colorPicker.tsx b/hooks/08_ColorPickerRefactor/src/components/colorPicker.tsx
index 945ab4c1..ff4cbcdc 100644
--- a/hooks/08_ColorPickerRefactor/src/components/colorPicker.tsx
+++ b/hooks/08_ColorPickerRefactor/src/components/colorPicker.tsx
@@ -10,11 +10,11 @@ const updateColor = (props: Props, colorId: keyof Color) => (value: any) => {
// keyof Color ensures only 'red', 'blue' or 'green' can be passed in.
props.onColorUpdated({
...props.color, // this creates a clone of the current props.color object...
- [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value.
+ [colorId]: value, // ... which gets one of its properties (colorId) immediately replaced by a new value.
});
};
-export const ColorPicker = (props: Props) => (
+export const ColorPicker: React.FC = (props) => (
void;
}
-const ColorSliderComponent = (props: PropsColorSlider) => {
+const ColorSliderComponent: React.FC = (props: PropsColorSlider) => {
return (
{
min="0"
max="255"
value={props.value}
- onChange={event => props.onValueUpdated(+event.target.value)}
+ onChange={(event) => props.onValueUpdated(+event.target.value)}
/>
{props.value}
diff --git a/hooks/08_ColorPickerRefactor/src/components/hello.tsx b/hooks/08_ColorPickerRefactor/src/components/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/08_ColorPickerRefactor/src/components/hello.tsx
+++ b/hooks/08_ColorPickerRefactor/src/components/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/08_ColorPickerRefactor/src/components/nameEdit.tsx b/hooks/08_ColorPickerRefactor/src/components/nameEdit.tsx
index 646a4d26..7f9091e3 100644
--- a/hooks/08_ColorPickerRefactor/src/components/nameEdit.tsx
+++ b/hooks/08_ColorPickerRefactor/src/components/nameEdit.tsx
@@ -5,10 +5,10 @@ interface Props {
editingName: string;
onNameUpdated: () => any;
onEditingNameUpdated: (newEditingName: string) => any;
- disabled : boolean;
+ disabled: boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
@@ -21,10 +21,9 @@ export const NameEditComponent = (props: Props) => {
<>
Update name:
- Change
+
+ Change
+
>
);
};
diff --git a/hooks/08_ColorPickerRefactor/webpack.config.js b/hooks/08_ColorPickerRefactor/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/08_ColorPickerRefactor/webpack.config.js
+++ b/hooks/08_ColorPickerRefactor/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/09_Sidebar/Readme.md b/hooks/09_Sidebar/Readme.md
index be7bddb2..e3dda427 100644
--- a/hooks/09_Sidebar/Readme.md
+++ b/hooks/09_Sidebar/Readme.md
@@ -89,9 +89,10 @@ _./webpack.config.js_
+ {
+ loader: 'css-loader',
+ options: {
-+ modules: true,
-+ localIdentName: '[name]__[local]___[hash:base64:5]',
-+ camelCase: true,
++ modules: {
++ localIdentName: "[name]__[local]___[hash:base64:5]",
++ },
++ localsConvention: "camelCase",
+ },
+ },
+ ]
diff --git a/hooks/09_Sidebar/package.json b/hooks/09_Sidebar/package.json
index eadce68d..40171b2f 100644
--- a/hooks/09_Sidebar/package.json
+++ b/hooks/09_Sidebar/package.json
@@ -15,28 +15,28 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/node": "^11.9.4",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/09_Sidebar/src/components/colorBrowser.tsx b/hooks/09_Sidebar/src/components/colorBrowser.tsx
index 7dcc3905..813746ce 100644
--- a/hooks/09_Sidebar/src/components/colorBrowser.tsx
+++ b/hooks/09_Sidebar/src/components/colorBrowser.tsx
@@ -5,13 +5,11 @@ interface Props {
color: Color;
}
-export const ColorBrowser = (props: Props) => {
+export const ColorBrowser: React.FC = (props) => {
const divStyle: React.CSSProperties = {
width: "11rem",
height: "7rem",
- backgroundColor: `rgb(${props.color.red},${props.color.green}, ${
- props.color.blue
- })`
+ backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})`,
};
return
;
diff --git a/hooks/09_Sidebar/src/components/colorPicker.tsx b/hooks/09_Sidebar/src/components/colorPicker.tsx
index ea449834..4346cd4d 100644
--- a/hooks/09_Sidebar/src/components/colorPicker.tsx
+++ b/hooks/09_Sidebar/src/components/colorPicker.tsx
@@ -6,15 +6,15 @@ interface Props {
onColorUpdated: (color: Color) => void;
}
-const updateColor = (props: Props, colorId: keyof Color) => value => {
+const updateColor = (props: Props, colorId: keyof Color) => (value) => {
// keyof Color ensures only 'red', 'blue' or 'green' can be passed in.
props.onColorUpdated({
...props.color, // this creates a clone of the current props.color object...
- [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value.
+ [colorId]: value, // ... which gets one of its properties (colorId) immediately replaced by a new value.
});
};
-export const ColorPicker = (props: Props) => (
+export const ColorPicker: React.FC = (props) => (
void;
}
-const ColorSliderComponent = (props: PropsColorSlider) => {
+const ColorSliderComponent: React.FC = (props) => {
return (
{
min="0"
max="255"
value={props.value}
- onChange={event => props.onValueUpdated(+event.target.value)}
+ onChange={(event) => props.onValueUpdated(+event.target.value)}
/>
{props.value}
diff --git a/hooks/09_Sidebar/src/components/hello.tsx b/hooks/09_Sidebar/src/components/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/09_Sidebar/src/components/hello.tsx
+++ b/hooks/09_Sidebar/src/components/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/09_Sidebar/src/components/nameEdit.tsx b/hooks/09_Sidebar/src/components/nameEdit.tsx
index 646a4d26..7f9091e3 100644
--- a/hooks/09_Sidebar/src/components/nameEdit.tsx
+++ b/hooks/09_Sidebar/src/components/nameEdit.tsx
@@ -5,10 +5,10 @@ interface Props {
editingName: string;
onNameUpdated: () => any;
onEditingNameUpdated: (newEditingName: string) => any;
- disabled : boolean;
+ disabled: boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
@@ -21,10 +21,9 @@ export const NameEditComponent = (props: Props) => {
<>
Update name:
- Change
+
+ Change
+
>
);
};
diff --git a/hooks/09_Sidebar/src/components/sidebar.tsx b/hooks/09_Sidebar/src/components/sidebar.tsx
index 762991eb..0847a552 100644
--- a/hooks/09_Sidebar/src/components/sidebar.tsx
+++ b/hooks/09_Sidebar/src/components/sidebar.tsx
@@ -7,13 +7,11 @@ interface Props {
}
const divStyle = (props: Props): React.CSSProperties => ({
- width: props.isVisible ? "23rem" : "0rem"
+ width: props.isVisible ? "23rem" : "0rem",
});
-export const SidebarComponent: React.StatelessComponent = props => (
+export const SidebarComponent: React.StatelessComponent = (props) => (
{props.children}
);
-
-
diff --git a/hooks/09_Sidebar/webpack.config.js b/hooks/09_Sidebar/webpack.config.js
index eebdc585..c9996585 100644
--- a/hooks/09_Sidebar/webpack.config.js
+++ b/hooks/09_Sidebar/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx", ".css"]
+ extensions: [".js", ".ts", ".tsx", ".css"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,13 +31,13 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
include: /node_modules/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.css$/,
@@ -47,32 +47,34 @@ module.exports = {
{
loader: "css-loader",
options: {
- modules: true,
- localIdentName: "[name]__[local]___[hash:base64:5]",
- camelCase: true
- }
- }
- ]
+ modules: {
+ localIdentName: "[name]__[local]___[hash:base64:5]",
+ },
+ localsConvention: "camelCase",
+ },
+ },
+ ],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/10_TableMock/package.json b/hooks/10_TableMock/package.json
index eadce68d..40171b2f 100644
--- a/hooks/10_TableMock/package.json
+++ b/hooks/10_TableMock/package.json
@@ -15,28 +15,28 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/node": "^11.9.4",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/10_TableMock/src/components/colorBrowser.tsx b/hooks/10_TableMock/src/components/colorBrowser.tsx
index 7dcc3905..813746ce 100644
--- a/hooks/10_TableMock/src/components/colorBrowser.tsx
+++ b/hooks/10_TableMock/src/components/colorBrowser.tsx
@@ -5,13 +5,11 @@ interface Props {
color: Color;
}
-export const ColorBrowser = (props: Props) => {
+export const ColorBrowser: React.FC = (props) => {
const divStyle: React.CSSProperties = {
width: "11rem",
height: "7rem",
- backgroundColor: `rgb(${props.color.red},${props.color.green}, ${
- props.color.blue
- })`
+ backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})`,
};
return
;
diff --git a/hooks/10_TableMock/src/components/colorPicker.tsx b/hooks/10_TableMock/src/components/colorPicker.tsx
index ea449834..4346cd4d 100644
--- a/hooks/10_TableMock/src/components/colorPicker.tsx
+++ b/hooks/10_TableMock/src/components/colorPicker.tsx
@@ -6,15 +6,15 @@ interface Props {
onColorUpdated: (color: Color) => void;
}
-const updateColor = (props: Props, colorId: keyof Color) => value => {
+const updateColor = (props: Props, colorId: keyof Color) => (value) => {
// keyof Color ensures only 'red', 'blue' or 'green' can be passed in.
props.onColorUpdated({
...props.color, // this creates a clone of the current props.color object...
- [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value.
+ [colorId]: value, // ... which gets one of its properties (colorId) immediately replaced by a new value.
});
};
-export const ColorPicker = (props: Props) => (
+export const ColorPicker: React.FC = (props) => (
void;
}
-const ColorSliderComponent = (props: PropsColorSlider) => {
+const ColorSliderComponent: React.FC = (props) => {
return (
{
min="0"
max="255"
value={props.value}
- onChange={event => props.onValueUpdated(+event.target.value)}
+ onChange={(event) => props.onValueUpdated(+event.target.value)}
/>
{props.value}
diff --git a/hooks/10_TableMock/src/components/hello.tsx b/hooks/10_TableMock/src/components/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/10_TableMock/src/components/hello.tsx
+++ b/hooks/10_TableMock/src/components/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/10_TableMock/src/components/nameEdit.tsx b/hooks/10_TableMock/src/components/nameEdit.tsx
index 646a4d26..7f9091e3 100644
--- a/hooks/10_TableMock/src/components/nameEdit.tsx
+++ b/hooks/10_TableMock/src/components/nameEdit.tsx
@@ -5,10 +5,10 @@ interface Props {
editingName: string;
onNameUpdated: () => any;
onEditingNameUpdated: (newEditingName: string) => any;
- disabled : boolean;
+ disabled: boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
@@ -21,10 +21,9 @@ export const NameEditComponent = (props: Props) => {
<>
Update name:
- Change
+
+ Change
+
>
);
};
diff --git a/hooks/10_TableMock/webpack.config.js b/hooks/10_TableMock/webpack.config.js
index eebdc585..c9996585 100644
--- a/hooks/10_TableMock/webpack.config.js
+++ b/hooks/10_TableMock/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx", ".css"]
+ extensions: [".js", ".ts", ".tsx", ".css"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,13 +31,13 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
include: /node_modules/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.css$/,
@@ -47,32 +47,34 @@ module.exports = {
{
loader: "css-loader",
options: {
- modules: true,
- localIdentName: "[name]__[local]___[hash:base64:5]",
- camelCase: true
- }
- }
- ]
+ modules: {
+ localIdentName: "[name]__[local]___[hash:base64:5]",
+ },
+ localsConvention: "camelCase",
+ },
+ },
+ ],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/11_TableAxios/.babelrc b/hooks/11_TableAxios/.babelrc
index 957cae3e..06770f7d 100644
--- a/hooks/11_TableAxios/.babelrc
+++ b/hooks/11_TableAxios/.babelrc
@@ -5,6 +5,9 @@
{
"useBuiltIns": "entry"
}
- ]
- ]
+ ],
+ "@babel/preset-typescript",
+ "@babel/preset-react"
+ ],
+ "plugins": ["@babel/plugin-transform-runtime"]
}
diff --git a/hooks/11_TableAxios/Readme.md b/hooks/11_TableAxios/Readme.md
index 4808c875..7a5332d0 100644
--- a/hooks/11_TableAxios/Readme.md
+++ b/hooks/11_TableAxios/Readme.md
@@ -67,6 +67,31 @@ export const getMembersCollection = (): Promise => {
+ }));
```
+- Adding async await calling member api
+
+To make the async await work, we should install
+- `npm install -D @babel/plugin-transform-runtime`
+- `npm install -P @babel/runtime`
+
+and add to the .babelrc
+
+_.babelrc_
+```bash
+ "plugins": ["@babel/plugin-transform-runtime"]
+```
+
+_./src/components/memberTable.tsx_
+```diff
+- const loadMemberCollection = () => {
+- getMembersCollection().then(memberCollection =>
+- setMemberCollection(memberCollection)
+- );
+
++ const loadMemberCollection = async () => {
++ const memberCollection = await getMembersCollection();
++ setMemberCollection(memberCollection);
+```
+
- Aaaand... we don't need to add any update on the rest of the application, why?
The function is providing the same contract, it returns a promise,
let's give a try:
diff --git a/hooks/11_TableAxios/package.json b/hooks/11_TableAxios/package.json
index 7ef95a14..0d0d92b0 100644
--- a/hooks/11_TableAxios/package.json
+++ b/hooks/11_TableAxios/package.json
@@ -15,29 +15,33 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/node": "^11.9.4",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/plugin-transform-runtime": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@babel/preset-react": "^7.10.4",
+ "@babel/preset-typescript": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "axios": "^0.18.0",
- "react": "^16.8.2",
- "react-dom": "^16.8.2"
+ "@babel/runtime": "^7.10.5",
+ "axios": "^0.19.2",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1"
}
}
diff --git a/hooks/11_TableAxios/src/components/colorBrowser.tsx b/hooks/11_TableAxios/src/components/colorBrowser.tsx
index 7dcc3905..813746ce 100644
--- a/hooks/11_TableAxios/src/components/colorBrowser.tsx
+++ b/hooks/11_TableAxios/src/components/colorBrowser.tsx
@@ -5,13 +5,11 @@ interface Props {
color: Color;
}
-export const ColorBrowser = (props: Props) => {
+export const ColorBrowser: React.FC = (props) => {
const divStyle: React.CSSProperties = {
width: "11rem",
height: "7rem",
- backgroundColor: `rgb(${props.color.red},${props.color.green}, ${
- props.color.blue
- })`
+ backgroundColor: `rgb(${props.color.red},${props.color.green}, ${props.color.blue})`,
};
return
;
diff --git a/hooks/11_TableAxios/src/components/colorPicker.tsx b/hooks/11_TableAxios/src/components/colorPicker.tsx
index ea449834..4346cd4d 100644
--- a/hooks/11_TableAxios/src/components/colorPicker.tsx
+++ b/hooks/11_TableAxios/src/components/colorPicker.tsx
@@ -6,15 +6,15 @@ interface Props {
onColorUpdated: (color: Color) => void;
}
-const updateColor = (props: Props, colorId: keyof Color) => value => {
+const updateColor = (props: Props, colorId: keyof Color) => (value) => {
// keyof Color ensures only 'red', 'blue' or 'green' can be passed in.
props.onColorUpdated({
...props.color, // this creates a clone of the current props.color object...
- [colorId]: value // ... which gets one of its properties (colorId) immediately replaced by a new value.
+ [colorId]: value, // ... which gets one of its properties (colorId) immediately replaced by a new value.
});
};
-export const ColorPicker = (props: Props) => (
+export const ColorPicker: React.FC = (props) => (
void;
}
-const ColorSliderComponent = (props: PropsColorSlider) => {
+const ColorSliderComponent: React.FC = (props) => {
return (
{
min="0"
max="255"
value={props.value}
- onChange={event => props.onValueUpdated(+event.target.value)}
+ onChange={(event) => props.onValueUpdated(+event.target.value)}
/>
{props.value}
diff --git a/hooks/11_TableAxios/src/components/hello.tsx b/hooks/11_TableAxios/src/components/hello.tsx
index 33a0ab73..b6b7aca4 100644
--- a/hooks/11_TableAxios/src/components/hello.tsx
+++ b/hooks/11_TableAxios/src/components/hello.tsx
@@ -4,6 +4,6 @@ interface Props {
userName: string;
}
-export const HelloComponent = (props: Props) => {
+export const HelloComponent: React.FC = (props) => {
return Hello user: {props.userName} ! ;
};
diff --git a/hooks/11_TableAxios/src/components/memberTable.tsx b/hooks/11_TableAxios/src/components/memberTable.tsx
index eee44798..4ae64fa6 100644
--- a/hooks/11_TableAxios/src/components/memberTable.tsx
+++ b/hooks/11_TableAxios/src/components/memberTable.tsx
@@ -7,10 +7,9 @@ const useMemberCollection = () => {
MemberEntity[]
>([]);
- const loadMemberCollection = () => {
- getMembersCollection().then(memberCollection =>
- setMemberCollection(memberCollection)
- );
+ const loadMemberCollection = async () => {
+ const memberCollection = await getMembersCollection();
+ setMemberCollection(memberCollection);
};
return { memberCollection, loadMemberCollection };
@@ -34,7 +33,7 @@ export const MemberTableComponent = () => {
- {memberCollection.map(member => (
+ {memberCollection.map((member) => (
))}
diff --git a/hooks/11_TableAxios/src/components/nameEdit.tsx b/hooks/11_TableAxios/src/components/nameEdit.tsx
index 646a4d26..7f9091e3 100644
--- a/hooks/11_TableAxios/src/components/nameEdit.tsx
+++ b/hooks/11_TableAxios/src/components/nameEdit.tsx
@@ -5,10 +5,10 @@ interface Props {
editingName: string;
onNameUpdated: () => any;
onEditingNameUpdated: (newEditingName: string) => any;
- disabled : boolean;
+ disabled: boolean;
}
-export const NameEditComponent = (props: Props) => {
+export const NameEditComponent: React.FC = (props) => {
const onChange = (e: React.ChangeEvent) => {
props.onEditingNameUpdated(e.target.value);
};
@@ -21,10 +21,9 @@ export const NameEditComponent = (props: Props) => {
<>
Update name:
- Change
+
+ Change
+
>
);
};
diff --git a/hooks/11_TableAxios/webpack.config.js b/hooks/11_TableAxios/webpack.config.js
index eebdc585..c9996585 100644
--- a/hooks/11_TableAxios/webpack.config.js
+++ b/hooks/11_TableAxios/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx", ".css"]
+ extensions: [".js", ".ts", ".tsx", ".css"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,13 +31,13 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
include: /node_modules/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.css$/,
@@ -47,32 +47,34 @@ module.exports = {
{
loader: "css-loader",
options: {
- modules: true,
- localIdentName: "[name]__[local]___[hash:base64:5]",
- camelCase: true
- }
- }
- ]
+ modules: {
+ localIdentName: "[name]__[local]___[hash:base64:5]",
+ },
+ localsConvention: "camelCase",
+ },
+ },
+ ],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/12_ReactRouter/.babelrc b/hooks/12_ReactRouter/.babelrc
index 957cae3e..d09d473e 100644
--- a/hooks/12_ReactRouter/.babelrc
+++ b/hooks/12_ReactRouter/.babelrc
@@ -5,6 +5,8 @@
{
"useBuiltIns": "entry"
}
- ]
+ ],
+ "@babel/preset-typescript",
+ "@babel/preset-react"
]
}
diff --git a/hooks/12_ReactRouter/package.json b/hooks/12_ReactRouter/package.json
index 2ff4e9af..fb377361 100644
--- a/hooks/12_ReactRouter/package.json
+++ b/hooks/12_ReactRouter/package.json
@@ -15,29 +15,32 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
- "@types/react-router-dom": "^4.3.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@babel/preset-react": "^7.10.4",
+ "@babel/preset-typescript": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-router-dom": "^5.1.5",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "react": "^16.8.2",
- "react-dom": "^16.8.2",
- "react-router-dom": "^4.3.1"
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.2.0"
}
}
diff --git a/hooks/12_ReactRouter/webpack.config.js b/hooks/12_ReactRouter/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/12_ReactRouter/webpack.config.js
+++ b/hooks/12_ReactRouter/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/13_LoginForm/Readme.md b/hooks/13_LoginForm/Readme.md
index b505d8ba..5cef8683 100644
--- a/hooks/13_LoginForm/Readme.md
+++ b/hooks/13_LoginForm/Readme.md
@@ -18,14 +18,14 @@ npm install
- Let's update as well the name of the component.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.component.tsx_
```diff
import * as React from "react";
import { Link } from "react-router-dom";
- export const PageA = () => (
-+ export const LoginPage = () => (
++ export const LoginComponent: React.FC = (props) => {
-
Hello from page A
+
Hello from login Page
@@ -43,7 +43,7 @@ _./src/app.tsx_
import * as React from "react";
import { HashRouter, Switch, Route } from "react-router-dom";
- import { PageA } from "./pages/pageA";
-+ import { LoginPage } from "./pages/loginPage";
++ import { LoginComponent } from "./pages/login.component";
import { PageB } from "./pages/pageB";
export const App = () => {
@@ -53,7 +53,7 @@ export const App = () => {
-
-+
++
@@ -118,7 +118,7 @@ npm install @material-ui/core @material-ui/icons --save
- Now we could create a login form it could look something like:
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```javascript
import * as React from "react";
@@ -143,7 +143,7 @@ const useStyles = makeStyles(theme =>
interface Props extends RouteComponentProps {}
-const LoginPageInner = (props: Props) => {
+export const LoginContainer: React.FC
= (props) => {
const classes = useStyles();
return (
@@ -167,8 +167,6 @@ const LoginPageInner = (props: Props) => {
);
};
-
-export const LoginPage = withRouter < Props > LoginPageInner;
```
- This can be ok, but if we take a deeper look to this component, we could break down into two, one is the card itself the other the form dialog, so it should finally look like:
@@ -186,10 +184,10 @@ export const LoginPage = withRouter < Props > LoginPageInner;
- Let's create the LoginForm component (append it to the loginPage file):
-_./src/pages/loginPage.tsx_
+_./src/pages/login.component.tsx_
```javascript
-const LoginForm = props => {
+export const LoginComponent: React.FC = (props) => {
return (
{
justifyContent: "center"
}}
>
-
-
-
+
+
+
Login
@@ -208,16 +216,16 @@ const LoginForm = props => {
};
```
-- And let's update the _loginPage.tsx_
+- And let's update the _login.container.tsx_
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
return (
-+
++
-
-
interface Props extends RouteComponentProps {}
-const LoginPageInner = (props) => {
+export const LoginContainer: React.FC = (props) => {
const { classes } = props;
+ const onLogin = () => {
@@ -282,14 +290,13 @@ const LoginPageInner = (props) => {
)
}
-export const LoginPage = withRouter(LoginPageInner);
```
- Let's add the navigation on button clicked (later on we will check for user and pwd) _form.tsx_.
In order to do this we have used react-router 4 "withRouter" HoC (High order component), and pass it
down to the LoginForm component.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.component.tsx_
```diff
+interface PropsForm {
@@ -351,62 +358,59 @@ _./src/api/login.ts_
import { LoginEntity } from "../model/login";
// Just a fake loginAPI
-export const isValidLogin = (loginInfo: LoginEntity): boolean =>
- loginInfo.login === "admin" && loginInfo.password === "test";
+export const isValidLogin = (loginInfo: LoginEntity): Promise =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ // mock call
+ resolve(loginInfo.login === "admin" && loginInfo.password === "test");
+ }, 500);
+ });
```
- Let's add the _api_ integration, plus navigation on login succeeded:
- First let's create a login state and add the api integration.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
+ import { LoginEntity, createEmptyLogin } from '../model/login';
+ import { isValidLogin } from '../api/login';
```
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
const LoginPageInner = (props: Props) => {
-+ const [loginInfo, setLoginInfo] = React.useState(createEmptyLogin());
++ const [loginInfo, setLoginInfo] = React.useState(
++ createEmptyLogin()
++ );
const { classes } = props;
- const onLogin = () => {
-+ if(isValidLogin(loginInfo)) {
- props.history.push("/pageB");
-+ }
+ const loginSucceeded = (isValid: boolean) => {
+ if (isValid) {
+ history.push("/pageB");
+ } else {
+ setShowAlert(true);
+ }
+ };
+
+ const handleLogin = (login: LoginEntity) => {
+ isValidLogin(login).then(loginSucceeded);
};
```
- Now let's read the data from the textfields components (user and password).
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
- const onLogin = () => {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
- }
- };
-
-+ const onUpdateLoginField = (name, value) => {
-+ setLoginInfo({
-+ ...loginInfo,
-+ [name]: value,
-+ })
-+ }
-
return (
-
++
);
@@ -414,23 +418,25 @@ _./src/pages/loginPage.tsx_
- And update _LoginForm_ props and textField onChange.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.component.tsx_
```diff
interface PropsForm {
- onLogin: () => void;
-+ onUpdateField: (name: string, value: any) => void;
-+ loginInfo : LoginEntity;
++ onLogin: (login: LoginEntity) => void;
}
-const LoginForm = (props: PropsForm) => {
-- const { onLogin } = props;
-+ const { onLogin, onUpdateField, loginInfo } = props;
-
-+ // TODO: Enhacement move this outside the stateless component discuss why is a good idea
+export const LoginComponent: React.FC = (props) => {
++ const [loginInfo, setLoginInfo] = React.useState(
++ const { onLogin } = props;
++ createEmptyLogin()
++ );
++ const classes = useFormStyles();
+ const onTexFieldChange = (fieldId) => (e) => {
-+ onUpdateField(fieldId, e.target.value);
-+ }
++ setLoginInfo({
++ ...loginInfo,
++ [fieldId]: e.target.value,
++ });
++ };
return (
{
justifyContent: "center"
}}
>
-
-
-
+ onLogin(loginInfo)}
+ >
Login
@@ -458,13 +473,11 @@ const LoginForm = (props: PropsForm) => {
- We will add material-ui classes to LoginForm component.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
interface PropsForm {
- onLogin: () => void;
- onUpdateField: (name: string, value: any) => void;
- loginInfo : LoginEntity;
+ onLogin: (login: LoginEntity) => void;
}
+ // https://material-ui.com/styles/api/#makestyles-styles-options-hook
@@ -478,14 +491,8 @@ interface PropsForm {
+ })
+ );
-const LoginForm = (props: PropsForm) => {
+export const LoginComponent: React.FC = (props) => {
+ const classes = useFormStyles();
- const { onLogin, onUpdateField, loginInfo } = props;
-
- // TODO: Enhacement move this outside the stateless component discuss why is a good idea
- const onTexFieldChange = (fieldId) => (e) => {
- onUpdateField(fieldId, e.target.value);
- }
return (
- {
const [loginInfo, setLoginInfo] = React.useState
(
createEmptyLogin()
);
-+ const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
++ const [isShowAlert, setShowAlert] = React.useState(false);
const classes = useStyles();
const onLogin = () => {
@@ -602,7 +609,7 @@ const LoginPageInner = (props: Props) => {
props.history.push("/pageB");
- }
+ } else {
-+ setShowLoginFailedMsg(true);
++ setShowAlert(true);
+ }
}
@@ -625,11 +632,11 @@ const LoginPageInner = (props: Props) => {
/>
-+ setShowLoginFailedMsg(false)}
-+ />
++ setShowAlert(false)}
++ />
+ >
);
};
diff --git a/hooks/13_LoginForm/package.json b/hooks/13_LoginForm/package.json
index d22ed28f..258ceb77 100644
--- a/hooks/13_LoginForm/package.json
+++ b/hooks/13_LoginForm/package.json
@@ -15,31 +15,34 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
- "@types/react-router-dom": "^4.3.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@babel/preset-react": "^7.10.4",
+ "@babel/preset-typescript": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-router-dom": "^5.1.5",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "@material-ui/core": "^4.0.1",
- "@material-ui/icons": "^4.0.1",
- "react": "^16.8.2",
- "react-dom": "^16.8.2",
- "react-router-dom": "^4.3.1"
+ "@material-ui/core": "^4.11.0",
+ "@material-ui/icons": "^4.9.1",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.2.0"
}
}
diff --git a/hooks/13_LoginForm/src/api/login.ts b/hooks/13_LoginForm/src/api/login.ts
index 1f7d4f3b..6fd3e26c 100644
--- a/hooks/13_LoginForm/src/api/login.ts
+++ b/hooks/13_LoginForm/src/api/login.ts
@@ -1,5 +1,10 @@
-import {LoginEntity} from '../model/login';
+import { LoginEntity } from "../model/login";
// Just a fake loginAPI
-export const isValidLogin = (loginInfo : LoginEntity) : boolean =>
- (loginInfo.login === 'admin' && loginInfo.password === 'test');
+export const isValidLogin = (loginInfo: LoginEntity): Promise =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ // mock call
+ resolve(loginInfo.login === "admin" && loginInfo.password === "test");
+ }, 500);
+ });
diff --git a/hooks/13_LoginForm/src/app.tsx b/hooks/13_LoginForm/src/app.tsx
index 9c1b1a24..169d50d8 100644
--- a/hooks/13_LoginForm/src/app.tsx
+++ b/hooks/13_LoginForm/src/app.tsx
@@ -1,6 +1,6 @@
import * as React from "react";
import { HashRouter, Switch, Route } from "react-router-dom";
-import { LoginPage } from "./pages/loginPage";
+import { LoginContainer } from "./pages/login.container";
import { PageB } from "./pages/pageB";
export const App = () => {
@@ -8,7 +8,7 @@ export const App = () => {
<>
-
+
diff --git a/hooks/13_LoginForm/src/common/notification.tsx b/hooks/13_LoginForm/src/common/notification.tsx
index 3fc14394..50e2b706 100644
--- a/hooks/13_LoginForm/src/common/notification.tsx
+++ b/hooks/13_LoginForm/src/common/notification.tsx
@@ -11,29 +11,29 @@ interface Props {
onClose: () => void;
}
-const useStyles = makeStyles(theme =>
+const useStyles = makeStyles((theme) =>
createStyles({
close: {
- padding: theme.spacing(0.5)
- }
+ padding: theme.spacing(0.5),
+ },
})
);
-export const NotificationComponent = (props: Props) => {
+export const NotificationComponent: React.FC = (props) => {
const classes = useStyles();
const { message, show, onClose } = props;
return (
{message}}
action={[
@@ -45,7 +45,7 @@ export const NotificationComponent = (props: Props) => {
onClick={onClose}
>
-
+ ,
]}
/>
);
diff --git a/hooks/13_LoginForm/src/pages/login.component.tsx b/hooks/13_LoginForm/src/pages/login.component.tsx
new file mode 100644
index 00000000..c78a4851
--- /dev/null
+++ b/hooks/13_LoginForm/src/pages/login.component.tsx
@@ -0,0 +1,60 @@
+import * as React from "react";
+import makeStyles from "@material-ui/styles/makeStyles";
+import createStyles from "@material-ui/styles/createStyles";
+import TextField from "@material-ui/core/TextField";
+import Button from "@material-ui/core/Button";
+import { LoginEntity, createEmptyLogin } from "../model/login";
+
+interface PropsForm {
+ onLogin: (login: LoginEntity) => void;
+}
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ formContainer: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ },
+ })
+);
+
+export const LoginComponent: React.FC = (props) => {
+ const { onLogin } = props;
+ const [loginInfo, setLoginInfo] = React.useState(
+ createEmptyLogin()
+ );
+ const classes = useFormStyles();
+ const onTexFieldChange = (fieldId) => (e) => {
+ setLoginInfo({
+ ...loginInfo,
+ [fieldId]: e.target.value,
+ });
+ };
+
+ return (
+
+
+
+ onLogin(loginInfo)}
+ >
+ Login
+
+
+ );
+};
diff --git a/hooks/13_LoginForm/src/pages/login.container.tsx b/hooks/13_LoginForm/src/pages/login.container.tsx
new file mode 100644
index 00000000..943c433b
--- /dev/null
+++ b/hooks/13_LoginForm/src/pages/login.container.tsx
@@ -0,0 +1,57 @@
+import * as React from "react";
+import { useHistory } from "react-router-dom";
+import { LoginEntity } from "../model/login";
+import { isValidLogin } from "../api/login";
+import { LoginComponent } from "./login.component";
+import Card from "@material-ui/core/Card";
+import CardHeader from "@material-ui/core/CardHeader";
+import CardContent from "@material-ui/core/CardContent";
+import createStyles from "@material-ui/styles/createStyles";
+import makeStyles from "@material-ui/styles/makeStyles";
+import { NotificationComponent } from "../common";
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ card: {
+ maxWidth: 400,
+ margin: "0 auto",
+ },
+ })
+);
+
+interface Props {}
+
+export const LoginContainer: React.FC = (props) => {
+ const history = useHistory();
+ const [isShowAlert, setShowAlert] = React.useState(false);
+ const classes = useFormStyles();
+
+ const loginSucceeded = (isValid: boolean) => {
+ if (isValid) {
+ history.push("/pageB");
+ } else {
+ setShowAlert(true);
+ }
+ };
+
+ const handleLogin = (login: LoginEntity) => {
+ isValidLogin(login).then(loginSucceeded);
+ };
+
+ return (
+ <>
+
+
+
+
+ setShowAlert(false)}
+ />
+
+
+ >
+ );
+};
diff --git a/hooks/13_LoginForm/src/pages/loginPage.tsx b/hooks/13_LoginForm/src/pages/loginPage.tsx
deleted file mode 100644
index 05e51a77..00000000
--- a/hooks/13_LoginForm/src/pages/loginPage.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import * as React from "react";
-import { withRouter, RouteComponentProps } from "react-router-dom";
-import makeStyles from "@material-ui/styles/makeStyles";
-import createStyles from "@material-ui/styles/createStyles";
-import Card from "@material-ui/core/Card";
-import CardHeader from "@material-ui/core/CardHeader";
-import CardContent from "@material-ui/core/CardContent";
-import TextField from "@material-ui/core/TextField";
-import Button from "@material-ui/core/Button";
-import { LoginEntity, createEmptyLogin } from "../model/login";
-import { isValidLogin } from "../api/login";
-import { NotificationComponent } from "../common";
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useStyles = makeStyles(theme =>
- createStyles({
- card: {
- maxWidth: 400,
- margin: "0 auto"
- }
- })
-);
-
-interface Props extends RouteComponentProps {}
-
-const LoginPageInner = (props: Props) => {
- const [loginInfo, setLoginInfo] = React.useState(
- createEmptyLogin()
- );
- const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
- const classes = useStyles();
-
- const onLogin = () => {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
- } else {
- setShowLoginFailedMsg(true);
- }
- };
-
- const onUpdateLoginField = (name, value) => {
- setLoginInfo({
- ...loginInfo,
- [name]: value
- });
- };
-
- return (
- <>
-
-
-
-
-
-
- setShowLoginFailedMsg(false)}
- />
- >
- );
-};
-
-export const LoginPage = withRouter(LoginPageInner);
-
-interface PropsForm {
- onLogin: () => void;
- onUpdateField: (name: string, value: any) => void;
- loginInfo: LoginEntity;
-}
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useFormStyles = makeStyles(theme =>
- createStyles({
- formContainer: {
- display: "flex",
- flexDirection: "column",
- justifyContent: "center"
- }
- })
-);
-
-const LoginForm = (props: PropsForm) => {
- const classes = useFormStyles();
- const { onLogin, onUpdateField, loginInfo } = props;
-
- // TODO: Enhacement move this outside the stateless component discuss why is a good idea
- const onTexFieldChange = fieldId => e => {
- onUpdateField(fieldId, e.target.value);
- };
-
- return (
-
-
-
-
- Login
-
-
- );
-};
diff --git a/hooks/13_LoginForm/webpack.config.js b/hooks/13_LoginForm/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/13_LoginForm/webpack.config.js
+++ b/hooks/13_LoginForm/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/14_FormValidation/Readme.md b/hooks/14_FormValidation/Readme.md
index f906f597..f8538d50 100644
--- a/hooks/14_FormValidation/Readme.md
+++ b/hooks/14_FormValidation/Readme.md
@@ -2,11 +2,11 @@
Let's add validation support to this form.
-For this we will use lc-form-validation library
+For this we will use formik, fonk and fonk-formik library
Summary steps:
-- Install lc-form-validation library.
+- Install formik, fonk, fonk-formik library.
- Refactor input component to a common component and include error validation info.
- Let's define the validation for the form.
- Let's hook it.
@@ -19,56 +19,40 @@ Summary steps:
npm install
```
-- Let's install the _lc-form-validation-library_.
+- Let's install the _formik, @lemoncode/fonk, @lemoncode/fonk-formik _.
```bash
-npm install lc-form-validation --save
+npm install formik @lemoncode/fonk @lemoncode/fonk-formik --save
```
- To avoid having too much repeated code let's move to common an input component, including it's
label plus validation text.
-_./common/textFieldForm.tsx_
+_./common/textField.component.tsx_
```tsx
import * as React from "react";
-import TextField from "@material-ui/core/TextField";
-import Typography from "@material-ui/core/Typography/Typography";
-
-interface Props {
- name: string;
- label: string;
- onChange: any;
- value: string;
- error?: string;
- type?: string;
-}
-
-const defaultProps: Partial = {
- type: "text"
-};
+import { useField } from "formik";
+import MuiTextField, { TextFieldProps } from "@material-ui/core/TextField";
-const onTextFieldChange = (
- fieldId: string,
- onChange: (fieldId, value) => void
-) => e => {
- onChange(fieldId, e.target.value);
-};
+export const TextFieldComponent: React.FC = (props) => {
+ const [field, meta] = useField(props.name);
+ const textFieldProps = Boolean(field) ? field : props;
+ const hasError = Boolean(meta && meta.touched && meta.error);
-export const TextFieldForm: React.StatelessComponent = props => {
- const { name, label, onChange, value, error, type } = props;
return (
<>
-
-
- {props.error}
-
>
);
};
@@ -80,200 +64,87 @@ _./src/common/index.ts_
```diff
export * from './notification';
-+ export * from './textFieldForm';
++ export * from './TextFieldComponent';
```
- Now let's define a basic validation for the form, we want to ensure both fields are informed.
-_./src/pages/loginPage.validation.ts_
+_./src/pages/login.validation.ts_
```typescript
-import {
- createFormValidation,
- ValidationConstraints,
- Validators
-} from "lc-form-validation";
-
-const loginFormValidationConstraints: ValidationConstraints = {
- fields: {
- login: [{ validator: Validators.required }],
- password: [{ validator: Validators.required }]
- }
+import { ValidationSchema, Validators } from "@lemoncode/fonk";
+import { createFormikValidation } from "@lemoncode/fonk-formik";
+
+const validationSchema: ValidationSchema = {
+ field: {
+ login: [Validators.required],
+ password: [Validators.required],
+ },
};
-export const loginFormValidation = createFormValidation(
- loginFormValidationConstraints
-);
-```
-
-- Let's create now a class to hold the dataFormErrors.
-
-_./src/pages/loginPage.viewmodel.ts_
-
-```typescript
-import { FieldValidationResult } from "lc-form-validation";
-
-export interface LoginFormErrors {
- login: FieldValidationResult;
- password: FieldValidationResult;
-}
-
-export const createDefaultLoginFormErrors = (): LoginFormErrors => ({
- login: new FieldValidationResult(),
- password: new FieldValidationResult()
-});
+export const loginFormValidation = createFormikValidation(validationSchema);
```
- Now let's go for the component side.
- First let's add the dataFormErrors to the state of the component.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
import { isValidLogin } from "../api/login";
import { NotificationComponent } from "../common";
-+ import {LoginFormErrors, createDefaultLoginFormErrors} from './loginPage.viewmodel';
-```
-
-_./src/pages/loginPage.tsx_
-
-```diff
-const LoginPageInner = (props: Props) => {
- const [loginInfo, setLoginInfo] = React.useState(
- createEmptyLogin()
- );
-+ const [loginFormErrors, setLoginFormErrors] = React.useState(createDefaultLoginFormErrors());
- const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
-```
-
-- Let's fire the validation on viewmodel update.
-
-_./src/pages/loginPage.tsx_
-
-```diff
-+ import { loginFormValidation } from "./loginPage.validation";
-```
-
-_./src/pages/loginPage.tsx_
-
-```diff
-const onUpdateLoginField = (name, value) => {
- setLoginInfo({
- ...loginInfo,
- [name]: value
- });
-
-+ loginFormValidation.validateField(loginInfo, name, value)
-+ .then((fieldValidationResult) => {
-
-+ setLoginFormErrors({
-+ ...loginFormErrors,
-+ [name]: fieldValidationResult,
-+ });
-+ });
- };
```
-- We need to pass down dataFormErrors
-
-_./src/pages/loginPage.tsx_
-```diff
-
-```
-
-_./src/pages/loginPage.tsx_
-
-```diff
-interface PropsForm {
- onLogin: () => void;
- onUpdateField: (string, any) => void;
- loginInfo: LoginEntity;
-+ loginFormErrors : LoginFormErrors;
-}
-```
-
-- Let's replace the _TextFieldForm_ entries with the wrapper we have created (includes
+- Let's fire the validation on viewmodel update and replace the _TextFieldForm_ entries with the wrapper we have created (includes
displaying validation errors).
-_./src/pages/loginPage.tsx_
-
-```diff
-+ import { TextFieldForm } from '../common';
-```
-
-_./src/pages/loginPage.tsx_
+_./src/pages/login.component.tsx_
```diff
-const LoginForm = (props: PropsForm) => {
-- const { onLogin, onUpdateField, loginInfo } = props;
-+ const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props;
-```
-
-```diff
--
--
++ import { loginFormValidation } from "./login.validation";
++ import { TextFieldComponent } from '../common';
+
++
++ {() => (
++
++ )}
++
```
- Let's give a try
-```
+```bash
npm start
```
- And let's add an alert (Excercise and a notification) when the user clicks and the form all the fields are valid.
-_./src/pages/loginPage.tsx_
-
-```diff
-const onLogin = () => {
-+ loginFormValidation.validateForm(loginInfo)
-+ .then((formValidationResult) => {
-+ if(formValidationResult.succeeded) {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
- } else {
- setShowLoginFailedMsg(true);
- }
-+ } else {
-+ alert('error, review the fields');
-+ const updatedLoginFormErrors = {
-+ ...loginFormErrors,
-+ ...formValidationResult.fieldErrors,
-+ }
-+ setLoginFormErrors(updatedLoginFormErrors);
-+ }
-
-
-+ });
-};
-```
-
> Excercise, refactor this method following single abstraction level principle and single responsibility principle.
# About Basefactor + Lemoncode
diff --git a/hooks/14_FormValidation/package.json b/hooks/14_FormValidation/package.json
index 2914eb69..79169511 100644
--- a/hooks/14_FormValidation/package.json
+++ b/hooks/14_FormValidation/package.json
@@ -15,32 +15,37 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
- "@types/react-router-dom": "^4.3.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@babel/preset-react": "^7.10.4",
+ "@babel/preset-typescript": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-router-dom": "^5.1.5",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "@material-ui/core": "^4.0.1",
- "@material-ui/icons": "^4.0.1",
- "lc-form-validation": "^2.0.0",
- "react": "^16.8.2",
- "react-dom": "^16.8.2",
- "react-router-dom": "^4.3.1"
+ "@lemoncode/fonk": "^1.3.0",
+ "@lemoncode/fonk-formik": "^4.0.1",
+ "@material-ui/core": "^4.11.0",
+ "@material-ui/icons": "^4.9.1",
+ "formik": "^2.1.5",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.2.0"
}
}
diff --git a/hooks/14_FormValidation/src/api/login.ts b/hooks/14_FormValidation/src/api/login.ts
index 1f7d4f3b..6fd3e26c 100644
--- a/hooks/14_FormValidation/src/api/login.ts
+++ b/hooks/14_FormValidation/src/api/login.ts
@@ -1,5 +1,10 @@
-import {LoginEntity} from '../model/login';
+import { LoginEntity } from "../model/login";
// Just a fake loginAPI
-export const isValidLogin = (loginInfo : LoginEntity) : boolean =>
- (loginInfo.login === 'admin' && loginInfo.password === 'test');
+export const isValidLogin = (loginInfo: LoginEntity): Promise =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ // mock call
+ resolve(loginInfo.login === "admin" && loginInfo.password === "test");
+ }, 500);
+ });
diff --git a/hooks/14_FormValidation/src/app.tsx b/hooks/14_FormValidation/src/app.tsx
index 9c1b1a24..169d50d8 100644
--- a/hooks/14_FormValidation/src/app.tsx
+++ b/hooks/14_FormValidation/src/app.tsx
@@ -1,6 +1,6 @@
import * as React from "react";
import { HashRouter, Switch, Route } from "react-router-dom";
-import { LoginPage } from "./pages/loginPage";
+import { LoginContainer } from "./pages/login.container";
import { PageB } from "./pages/pageB";
export const App = () => {
@@ -8,7 +8,7 @@ export const App = () => {
<>
-
+
diff --git a/hooks/14_FormValidation/src/common/index.ts b/hooks/14_FormValidation/src/common/index.ts
index 2916465b..f377fb57 100644
--- a/hooks/14_FormValidation/src/common/index.ts
+++ b/hooks/14_FormValidation/src/common/index.ts
@@ -1,2 +1,2 @@
export * from './notification';
-export * from './textFieldForm';
\ No newline at end of file
+export * from './textField.component';
\ No newline at end of file
diff --git a/hooks/14_FormValidation/src/common/notification.tsx b/hooks/14_FormValidation/src/common/notification.tsx
index 3fc14394..50e2b706 100644
--- a/hooks/14_FormValidation/src/common/notification.tsx
+++ b/hooks/14_FormValidation/src/common/notification.tsx
@@ -11,29 +11,29 @@ interface Props {
onClose: () => void;
}
-const useStyles = makeStyles(theme =>
+const useStyles = makeStyles((theme) =>
createStyles({
close: {
- padding: theme.spacing(0.5)
- }
+ padding: theme.spacing(0.5),
+ },
})
);
-export const NotificationComponent = (props: Props) => {
+export const NotificationComponent: React.FC = (props) => {
const classes = useStyles();
const { message, show, onClose } = props;
return (
{message}}
action={[
@@ -45,7 +45,7 @@ export const NotificationComponent = (props: Props) => {
onClick={onClose}
>
-
+ ,
]}
/>
);
diff --git a/hooks/14_FormValidation/src/common/textField.component.tsx b/hooks/14_FormValidation/src/common/textField.component.tsx
new file mode 100644
index 00000000..718e1173
--- /dev/null
+++ b/hooks/14_FormValidation/src/common/textField.component.tsx
@@ -0,0 +1,25 @@
+import * as React from "react";
+import { useField } from "formik";
+import MuiTextField, { TextFieldProps } from "@material-ui/core/TextField";
+
+export const TextFieldComponent: React.FC = (props) => {
+ const [field, meta] = useField(props.name);
+ const textFieldProps = Boolean(field) ? field : props;
+ const hasError = Boolean(meta && meta.touched && meta.error);
+
+ return (
+ <>
+
+ >
+ );
+};
diff --git a/hooks/14_FormValidation/src/common/textFieldForm.tsx b/hooks/14_FormValidation/src/common/textFieldForm.tsx
deleted file mode 100644
index 221010ef..00000000
--- a/hooks/14_FormValidation/src/common/textFieldForm.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as React from "react";
-import TextField from "@material-ui/core/TextField";
-import Typography from "@material-ui/core/Typography/Typography";
-
-interface Props {
- name: string;
- label: string;
- onChange: any;
- value: string;
- error?: string;
- type?: string;
-}
-
-const defaultProps: Partial = {
- type: "text"
-};
-
-const onTextFieldChange = (
- fieldId: string,
- onChange: (fieldId, value) => void
-) => e => {
- onChange(fieldId, e.target.value);
-};
-
-export const TextFieldForm: React.StatelessComponent = props => {
- const { name, label, onChange, value, error, type } = props;
- return (
- <>
-
-
- {props.error}
-
- >
- );
-};
diff --git a/hooks/14_FormValidation/src/pages/login.component.tsx b/hooks/14_FormValidation/src/pages/login.component.tsx
new file mode 100644
index 00000000..e968d5d6
--- /dev/null
+++ b/hooks/14_FormValidation/src/pages/login.component.tsx
@@ -0,0 +1,53 @@
+import * as React from "react";
+import { LoginEntity, createEmptyLogin } from "../model/login";
+import { TextFieldComponent } from "../common";
+import { Form } from "formik";
+import createStyles from "@material-ui/styles/createStyles";
+import makeStyles from "@material-ui/styles/makeStyles";
+import Button from "@material-ui/core/Button";
+import { loginFormValidation } from "./login.validation";
+import { Formik } from "formik";
+
+interface PropsForm {
+ onLogin: (login: LoginEntity) => void;
+}
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ formContainer: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ },
+ })
+);
+
+export const LoginComponent: React.FC = (props) => {
+ const classes = useFormStyles();
+ const { onLogin } = props;
+
+ return (
+
+ {() => (
+
+ )}
+
+ );
+};
diff --git a/hooks/14_FormValidation/src/pages/login.container.tsx b/hooks/14_FormValidation/src/pages/login.container.tsx
new file mode 100644
index 00000000..1da3ed87
--- /dev/null
+++ b/hooks/14_FormValidation/src/pages/login.container.tsx
@@ -0,0 +1,58 @@
+import * as React from "react";
+import { useHistory } from "react-router-dom";
+import { LoginEntity } from "../model/login";
+import { isValidLogin } from "../api/login";
+import { LoginComponent } from "./login.component";
+import Card from "@material-ui/core/Card";
+import CardHeader from "@material-ui/core/CardHeader";
+import CardContent from "@material-ui/core/CardContent";
+import createStyles from "@material-ui/styles/createStyles";
+import makeStyles from "@material-ui/styles/makeStyles";
+import { NotificationComponent } from "../common";
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ card: {
+ maxWidth: 400,
+ margin: "0 auto",
+ },
+ })
+);
+
+interface Props {}
+interface Props {}
+
+export const LoginContainer: React.FC = (props) => {
+ const history = useHistory();
+ const [isShowAlert, setShowAlert] = React.useState(false);
+ const classes = useFormStyles();
+
+ const loginSucceeded = (isValid: boolean) => {
+ if (isValid) {
+ history.push("/pageB");
+ } else {
+ setShowAlert(true);
+ }
+ };
+
+ const handleLogin = (login: LoginEntity) => {
+ isValidLogin(login).then(loginSucceeded);
+ };
+
+ return (
+ <>
+
+
+
+
+ setShowAlert(false)}
+ />
+
+
+ >
+ );
+};
diff --git a/hooks/14_FormValidation/src/pages/login.validation.ts b/hooks/14_FormValidation/src/pages/login.validation.ts
new file mode 100644
index 00000000..20942129
--- /dev/null
+++ b/hooks/14_FormValidation/src/pages/login.validation.ts
@@ -0,0 +1,11 @@
+import { ValidationSchema, Validators } from "@lemoncode/fonk";
+import { createFormikValidation } from "@lemoncode/fonk-formik";
+
+const validationSchema: ValidationSchema = {
+ field: {
+ login: [Validators.required],
+ password: [Validators.required],
+ },
+};
+
+export const loginFormValidation = createFormikValidation(validationSchema);
diff --git a/hooks/14_FormValidation/src/pages/loginPage.tsx b/hooks/14_FormValidation/src/pages/loginPage.tsx
deleted file mode 100644
index 5dce227a..00000000
--- a/hooks/14_FormValidation/src/pages/loginPage.tsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import * as React from "react";
-import { Link } from "react-router-dom";
-import { withRouter, RouteComponentProps } from "react-router-dom";
-import makeStyles from "@material-ui/styles/makeStyles";
-import createStyles from "@material-ui/styles/createStyles";
-import Card from "@material-ui/core/Card";
-import CardHeader from "@material-ui/core/CardHeader";
-import CardContent from "@material-ui/core/CardContent";
-import TextField from "@material-ui/core/TextField";
-import Button from "@material-ui/core/Button";
-import { FormHelperText } from "@material-ui/core";
-import { LoginEntity, createEmptyLogin } from "../model/login";
-import { isValidLogin } from "../api/login";
-import { NotificationComponent } from "../common";
-import {
- LoginFormErrors,
- createDefaultLoginFormErrors
-} from "./loginPage.viewmodel";
-import { loginFormValidation } from "./loginPage.validation";
-import { TextFieldForm } from "../common";
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useStyles = makeStyles(theme =>
- createStyles({
- card: {
- maxWidth: 400,
- margin: "0 auto"
- }
- })
-);
-
-interface Props extends RouteComponentProps {}
-
-const LoginPageInner = (props: Props) => {
- const [loginInfo, setLoginInfo] = React.useState(
- createEmptyLogin()
- );
- const [loginFormErrors, setLoginFormErrors] = React.useState(
- createDefaultLoginFormErrors()
- );
- const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
- const classes = useStyles();
-
- const onLogin = () => {
- loginFormValidation.validateForm(loginInfo).then(formValidationResult => {
- if (formValidationResult.succeeded) {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
- } else {
- setShowLoginFailedMsg(true);
- }
- } else {
- alert("error, review the fields");
- const updatedLoginFormErrors = {
- ...loginFormErrors,
- ...formValidationResult.fieldErrors
- };
- setLoginFormErrors(updatedLoginFormErrors);
- }
- });
- };
-
- const onUpdateLoginField = (name, value) => {
- setLoginInfo({
- ...loginInfo,
- [name]: value
- });
-
- loginFormValidation
- .validateField(loginInfo, name, value)
- .then(fieldValidationResult => {
- setLoginFormErrors({
- ...loginFormErrors,
- [name]: fieldValidationResult
- });
- });
- };
-
- return (
- <>
-
-
-
-
-
-
- setShowLoginFailedMsg(false)}
- />
- >
- );
-};
-
-export const LoginPage = withRouter(LoginPageInner);
-
-interface PropsForm {
- onLogin: () => void;
- onUpdateField: (string, any) => void;
- loginInfo: LoginEntity;
- loginFormErrors: LoginFormErrors;
-}
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useFormStyles = makeStyles(theme =>
- createStyles({
- formContainer: {
- display: "flex",
- flexDirection: "column",
- justifyContent: "center"
- }
- })
-);
-
-const LoginForm = (props: PropsForm) => {
- const classes = useFormStyles();
- const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props;
-
- // TODO: Enhacement move this outside the stateless component discuss why is a good idea
- const onTexFieldChange = fieldId => e => {
- onUpdateField(fieldId, e.target.value);
- };
-
- return (
-
-
-
-
- Login
-
-
- );
-};
diff --git a/hooks/14_FormValidation/src/pages/loginPage.validation.ts b/hooks/14_FormValidation/src/pages/loginPage.validation.ts
deleted file mode 100644
index ae379b5d..00000000
--- a/hooks/14_FormValidation/src/pages/loginPage.validation.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import {
- createFormValidation,
- ValidationConstraints,
- Validators
-} from "lc-form-validation";
-
-const loginFormValidationConstraints: ValidationConstraints = {
- fields: {
- login: [{ validator: Validators.required }],
- password: [{ validator: Validators.required }]
- }
-};
-
-export const loginFormValidation = createFormValidation(
- loginFormValidationConstraints
-);
diff --git a/hooks/14_FormValidation/src/pages/loginPage.viewmodel.ts b/hooks/14_FormValidation/src/pages/loginPage.viewmodel.ts
deleted file mode 100644
index 4b3cf5b4..00000000
--- a/hooks/14_FormValidation/src/pages/loginPage.viewmodel.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { FieldValidationResult } from "lc-form-validation";
-
-export interface LoginFormErrors {
- login: FieldValidationResult;
- password: FieldValidationResult;
-}
-
-export const createDefaultLoginFormErrors = (): LoginFormErrors => ({
- login: new FieldValidationResult(),
- password: new FieldValidationResult()
-});
diff --git a/hooks/14_FormValidation/webpack.config.js b/hooks/14_FormValidation/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/14_FormValidation/webpack.config.js
+++ b/hooks/14_FormValidation/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/hooks/15_Context/Readme.md b/hooks/15_Context/Readme.md
index 38ef6245..fb20ed96 100644
--- a/hooks/15_Context/Readme.md
+++ b/hooks/15_Context/Readme.md
@@ -119,7 +119,7 @@ import { Link } from "react-router-dom";
- Let's update the loginPage.
-_./src/pages/loginPage.tsx_
+_./src/pages/login.container.tsx_
```diff
- import { TextFieldForm } from "../common";
@@ -130,37 +130,25 @@ _./src/pages/loginPage.tsx_
_./src/pages/loginPage.tsx_
```diff
-const LoginPageInner = (props: Props) => {
-+ const loginContext = React.useContext(SessionContext);
+export const LoginContainer: React.FC = (props) => {
++ const loginContext = React.useContext(SessionContext);
+ const history = useHistory();
+ const [isShowAlert, setShowAlert] = React.useState(false);
+ const classes = useFormStyles();
+
+ const loginSucceeded = (isValid: boolean, login: LoginEntity) => {
+ if (isValid) {
+ history.push("/pageB");
++ loginContext.updateLogin(login.login);
+ } else {
+ setShowAlert(true);
+ }
+ };
- const [loginInfo, setLoginInfo] = React.useState(
- createEmptyLogin()
- );
- const [loginFormErrors, setLoginFormErrors] = React.useState(
- createDefaultLoginFormErrors()
- );
- const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
- const classes = useStyles();
-
- const onLogin = () => {
- loginFormValidation.validateForm(loginInfo).then(formValidationResult => {
- if (formValidationResult.succeeded) {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
-+ loginContext.updateLogin(loginInfo.login);
- } else {
- setShowLoginFailedMsg(true);
- }
- } else {
- alert("error, review the fields");
- const updatedLoginFormErrors = {
- ...loginFormErrors,
- ...formValidationResult.fieldErrors
- };
- setLoginFormErrors(updatedLoginFormErrors);
- }
- });
+ const handleLogin = (login: LoginEntity) => {
+ isValidLogin(login).then((isValid) => loginSucceeded(isValid, login));
};
+};
```
- Let's give a try.
diff --git a/hooks/15_Context/package.json b/hooks/15_Context/package.json
index 2914eb69..79169511 100644
--- a/hooks/15_Context/package.json
+++ b/hooks/15_Context/package.json
@@ -15,32 +15,37 @@
"author": "Braulio Diez Botella",
"license": "MIT",
"devDependencies": {
- "@babel/cli": "^7.2.3",
- "@babel/core": "^7.2.2",
- "@babel/polyfill": "^7.2.5",
- "@babel/preset-env": "^7.3.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.1",
- "@types/react-router-dom": "^4.3.1",
+ "@babel/cli": "^7.10.5",
+ "@babel/core": "^7.10.5",
+ "@babel/polyfill": "^7.10.4",
+ "@babel/preset-env": "^7.10.4",
+ "@babel/preset-react": "^7.10.4",
+ "@babel/preset-typescript": "^7.10.4",
+ "@types/node": "^14.0.24",
+ "@types/react": "^16.9.43",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-router-dom": "^5.1.5",
"awesome-typescript-loader": "^5.2.1",
- "babel-loader": "^8.0.5",
- "css-loader": "^2.1.0",
- "file-loader": "^3.0.1",
- "html-webpack-plugin": "^3.2.0",
- "mini-css-extract-plugin": "^0.5.0",
- "style-loader": "^0.23.1",
- "typescript": "^3.3.3",
- "url-loader": "^1.1.2",
- "webpack": "^4.29.3",
- "webpack-cli": "^3.2.3",
- "webpack-dev-server": "^3.1.14"
+ "babel-loader": "^8.1.0",
+ "css-loader": "^3.6.0",
+ "file-loader": "^6.0.0",
+ "html-webpack-plugin": "^4.3.0",
+ "mini-css-extract-plugin": "^0.9.0",
+ "style-loader": "^1.2.1",
+ "typescript": "^3.9.7",
+ "url-loader": "^4.1.0",
+ "webpack": "^4.43.0",
+ "webpack-cli": "^3.3.12",
+ "webpack-dev-server": "^3.11.0"
},
"dependencies": {
- "@material-ui/core": "^4.0.1",
- "@material-ui/icons": "^4.0.1",
- "lc-form-validation": "^2.0.0",
- "react": "^16.8.2",
- "react-dom": "^16.8.2",
- "react-router-dom": "^4.3.1"
+ "@lemoncode/fonk": "^1.3.0",
+ "@lemoncode/fonk-formik": "^4.0.1",
+ "@material-ui/core": "^4.11.0",
+ "@material-ui/icons": "^4.9.1",
+ "formik": "^2.1.5",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.2.0"
}
}
diff --git a/hooks/15_Context/src/api/login.ts b/hooks/15_Context/src/api/login.ts
index 1f7d4f3b..6fd3e26c 100644
--- a/hooks/15_Context/src/api/login.ts
+++ b/hooks/15_Context/src/api/login.ts
@@ -1,5 +1,10 @@
-import {LoginEntity} from '../model/login';
+import { LoginEntity } from "../model/login";
// Just a fake loginAPI
-export const isValidLogin = (loginInfo : LoginEntity) : boolean =>
- (loginInfo.login === 'admin' && loginInfo.password === 'test');
+export const isValidLogin = (loginInfo: LoginEntity): Promise =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ // mock call
+ resolve(loginInfo.login === "admin" && loginInfo.password === "test");
+ }, 500);
+ });
diff --git a/hooks/15_Context/src/app.tsx b/hooks/15_Context/src/app.tsx
index 44bd6549..89ae2660 100644
--- a/hooks/15_Context/src/app.tsx
+++ b/hooks/15_Context/src/app.tsx
@@ -1,6 +1,6 @@
import * as React from "react";
import { HashRouter, Switch, Route } from "react-router-dom";
-import { LoginPage } from "./pages/loginPage";
+import { LoginContainer } from "./pages/login.container";
import { PageB } from "./pages/pageB";
import { SessionProvider } from "./common";
@@ -10,7 +10,7 @@ export const App = () => {
-
+
diff --git a/hooks/15_Context/src/common/index.ts b/hooks/15_Context/src/common/index.ts
index c6185541..feefdf2a 100644
--- a/hooks/15_Context/src/common/index.ts
+++ b/hooks/15_Context/src/common/index.ts
@@ -1,3 +1,3 @@
export * from "./notification";
-export * from "./textFieldForm";
+export * from "./textField.component";
export * from "./sessionContext";
diff --git a/hooks/15_Context/src/common/notification.tsx b/hooks/15_Context/src/common/notification.tsx
index 3fc14394..50e2b706 100644
--- a/hooks/15_Context/src/common/notification.tsx
+++ b/hooks/15_Context/src/common/notification.tsx
@@ -11,29 +11,29 @@ interface Props {
onClose: () => void;
}
-const useStyles = makeStyles(theme =>
+const useStyles = makeStyles((theme) =>
createStyles({
close: {
- padding: theme.spacing(0.5)
- }
+ padding: theme.spacing(0.5),
+ },
})
);
-export const NotificationComponent = (props: Props) => {
+export const NotificationComponent: React.FC = (props) => {
const classes = useStyles();
const { message, show, onClose } = props;
return (
{message}}
action={[
@@ -45,7 +45,7 @@ export const NotificationComponent = (props: Props) => {
onClick={onClose}
>
-
+ ,
]}
/>
);
diff --git a/hooks/15_Context/src/common/sessionContext.tsx b/hooks/15_Context/src/common/sessionContext.tsx
index 4d1c0384..4edab12f 100644
--- a/hooks/15_Context/src/common/sessionContext.tsx
+++ b/hooks/15_Context/src/common/sessionContext.tsx
@@ -7,18 +7,18 @@ export interface SessionContextProps {
export const createDefaultUser = (): SessionContextProps => ({
login: "no user",
- updateLogin: value => {
+ updateLogin: (value) => {
console.warn(
"if you are reading this, likely you forgot to add the provider on top of your app"
);
- }
+ },
});
export const SessionContext = React.createContext(
createDefaultUser()
);
-export const SessionProvider: React.FunctionComponent = props => {
+export const SessionProvider: React.FC = (props) => {
const [login, setLogin] = React.useState("");
return (
diff --git a/hooks/15_Context/src/common/textField.component.tsx b/hooks/15_Context/src/common/textField.component.tsx
new file mode 100644
index 00000000..718e1173
--- /dev/null
+++ b/hooks/15_Context/src/common/textField.component.tsx
@@ -0,0 +1,25 @@
+import * as React from "react";
+import { useField } from "formik";
+import MuiTextField, { TextFieldProps } from "@material-ui/core/TextField";
+
+export const TextFieldComponent: React.FC = (props) => {
+ const [field, meta] = useField(props.name);
+ const textFieldProps = Boolean(field) ? field : props;
+ const hasError = Boolean(meta && meta.touched && meta.error);
+
+ return (
+ <>
+
+ >
+ );
+};
diff --git a/hooks/15_Context/src/common/textFieldForm.tsx b/hooks/15_Context/src/common/textFieldForm.tsx
deleted file mode 100644
index 221010ef..00000000
--- a/hooks/15_Context/src/common/textFieldForm.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import * as React from "react";
-import TextField from "@material-ui/core/TextField";
-import Typography from "@material-ui/core/Typography/Typography";
-
-interface Props {
- name: string;
- label: string;
- onChange: any;
- value: string;
- error?: string;
- type?: string;
-}
-
-const defaultProps: Partial = {
- type: "text"
-};
-
-const onTextFieldChange = (
- fieldId: string,
- onChange: (fieldId, value) => void
-) => e => {
- onChange(fieldId, e.target.value);
-};
-
-export const TextFieldForm: React.StatelessComponent = props => {
- const { name, label, onChange, value, error, type } = props;
- return (
- <>
-
-
- {props.error}
-
- >
- );
-};
diff --git a/hooks/15_Context/src/pages/login.component.tsx b/hooks/15_Context/src/pages/login.component.tsx
new file mode 100644
index 00000000..2c4adb0e
--- /dev/null
+++ b/hooks/15_Context/src/pages/login.component.tsx
@@ -0,0 +1,57 @@
+import * as React from "react";
+import { LoginEntity, createEmptyLogin } from "../model/login";
+import { TextFieldComponent } from "../common";
+import { Form } from "formik";
+import createStyles from "@material-ui/styles/createStyles";
+import makeStyles from "@material-ui/styles/makeStyles";
+import Button from "@material-ui/core/Button";
+import { loginFormValidation } from "./login.validation";
+import { Formik } from "formik";
+
+interface PropsForm {
+ onLogin: (login: LoginEntity) => void;
+}
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ formContainer: {
+ display: "flex",
+ flexDirection: "column",
+ justifyContent: "center",
+ },
+ card: {
+ maxWidth: 400,
+ margin: "0 auto",
+ },
+ })
+);
+
+export const LoginComponent: React.FC = (props) => {
+ const classes = useFormStyles();
+ const { onLogin } = props;
+
+ return (
+
+ {() => (
+
+ )}
+
+ );
+};
diff --git a/hooks/15_Context/src/pages/login.container.tsx b/hooks/15_Context/src/pages/login.container.tsx
new file mode 100644
index 00000000..e3e65ca0
--- /dev/null
+++ b/hooks/15_Context/src/pages/login.container.tsx
@@ -0,0 +1,59 @@
+import * as React from "react";
+import { useHistory } from "react-router-dom";
+import { LoginEntity } from "../model/login";
+import { isValidLogin } from "../api/login";
+import { LoginComponent } from "./login.component";
+import Card from "@material-ui/core/Card";
+import CardHeader from "@material-ui/core/CardHeader";
+import CardContent from "@material-ui/core/CardContent";
+import createStyles from "@material-ui/styles/createStyles";
+import makeStyles from "@material-ui/styles/makeStyles";
+import { NotificationComponent, SessionContext } from "../common";
+
+// https://material-ui.com/styles/api/#makestyles-styles-options-hook
+const useFormStyles = makeStyles((theme) =>
+ createStyles({
+ card: {
+ maxWidth: 400,
+ margin: "0 auto",
+ },
+ })
+);
+
+interface Props {}
+
+export const LoginContainer: React.FC = (props) => {
+ const loginContext = React.useContext(SessionContext);
+ const history = useHistory();
+ const [isShowAlert, setShowAlert] = React.useState(false);
+ const classes = useFormStyles();
+
+ const loginSucceeded = (isValid: boolean, login: LoginEntity) => {
+ if (isValid) {
+ history.push("/pageB");
+ loginContext.updateLogin(login.login);
+ } else {
+ setShowAlert(true);
+ }
+ };
+
+ const handleLogin = (login: LoginEntity) => {
+ isValidLogin(login).then((isValid) => loginSucceeded(isValid, login));
+ };
+
+ return (
+ <>
+
+
+
+
+ setShowAlert(false)}
+ />
+
+
+ >
+ );
+};
diff --git a/hooks/15_Context/src/pages/login.validation.ts b/hooks/15_Context/src/pages/login.validation.ts
new file mode 100644
index 00000000..20942129
--- /dev/null
+++ b/hooks/15_Context/src/pages/login.validation.ts
@@ -0,0 +1,11 @@
+import { ValidationSchema, Validators } from "@lemoncode/fonk";
+import { createFormikValidation } from "@lemoncode/fonk-formik";
+
+const validationSchema: ValidationSchema = {
+ field: {
+ login: [Validators.required],
+ password: [Validators.required],
+ },
+};
+
+export const loginFormValidation = createFormikValidation(validationSchema);
diff --git a/hooks/15_Context/src/pages/loginPage.tsx b/hooks/15_Context/src/pages/loginPage.tsx
deleted file mode 100644
index a2d10824..00000000
--- a/hooks/15_Context/src/pages/loginPage.tsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import * as React from "react";
-import { Link } from "react-router-dom";
-import { withRouter, RouteComponentProps } from "react-router-dom";
-import makeStyles from "@material-ui/styles/makestyles";
-import createStyles from "@material-ui/styles/createStyles";
-import Card from "@material-ui/core/Card";
-import CardHeader from "@material-ui/core/CardHeader";
-import CardContent from "@material-ui/core/CardContent";
-import TextField from "@material-ui/core/TextField";
-import Button from "@material-ui/core/Button";
-import { FormHelperText } from "@material-ui/core";
-import { LoginEntity, createEmptyLogin } from "../model/login";
-import { isValidLogin } from "../api/login";
-import { NotificationComponent } from "../common";
-import {
- LoginFormErrors,
- createDefaultLoginFormErrors
-} from "./loginPage.viewmodel";
-import { loginFormValidation } from "./loginPage.validation";
-import { TextFieldForm, SessionContext } from "../common";
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useStyles = makeStyles(theme =>
- createStyles({
- card: {
- maxWidth: 400,
- margin: "0 auto"
- }
- })
-);
-
-interface Props extends RouteComponentProps {}
-
-const LoginPageInner = (props: Props) => {
- const loginContext = React.useContext(SessionContext);
-
- const [loginInfo, setLoginInfo] = React.useState(
- createEmptyLogin()
- );
- const [loginFormErrors, setLoginFormErrors] = React.useState(
- createDefaultLoginFormErrors()
- );
- const [showLoginFailedMsg, setShowLoginFailedMsg] = React.useState(false);
- const classes = useStyles();
-
- const onLogin = () => {
- loginFormValidation.validateForm(loginInfo).then(formValidationResult => {
- if (formValidationResult.succeeded) {
- if (isValidLogin(loginInfo)) {
- props.history.push("/pageB");
- loginContext.updateLogin(loginInfo.login);
- } else {
- setShowLoginFailedMsg(true);
- }
- } else {
- alert("error, review the fields");
- const updatedLoginFormErrors = {
- ...loginFormErrors,
- ...formValidationResult.fieldErrors
- };
- setLoginFormErrors(updatedLoginFormErrors);
- }
- });
- };
-
- const onUpdateLoginField = (name, value) => {
- setLoginInfo({
- ...loginInfo,
- [name]: value
- });
-
- loginFormValidation
- .validateField(loginInfo, name, value)
- .then(fieldValidationResult => {
- setLoginFormErrors({
- ...loginFormErrors,
- [name]: fieldValidationResult
- });
- });
- };
-
- return (
- <>
-
-
-
-
-
-
- setShowLoginFailedMsg(false)}
- />
- >
- );
-};
-
-export const LoginPage = withRouter(LoginPageInner);
-
-interface PropsForm {
- onLogin: () => void;
- onUpdateField: (string, any) => void;
- loginInfo: LoginEntity;
- loginFormErrors: LoginFormErrors;
-}
-
-// https://material-ui.com/styles/api/#makestyles-styles-options-hook
-const useFormStyles = makeStyles(theme =>
- createStyles({
- formContainer: {
- display: "flex",
- flexDirection: "column",
- justifyContent: "center"
- }
- })
-);
-
-const LoginForm = (props: PropsForm) => {
- const classes = useFormStyles();
- const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props;
-
- // TODO: Enhacement move this outside the stateless component discuss why is a good idea
- const onTexFieldChange = fieldId => e => {
- onUpdateField(fieldId, e.target.value);
- };
-
- return (
-
-
-
-
- Login
-
-
- );
-};
diff --git a/hooks/15_Context/src/pages/loginPage.validation.ts b/hooks/15_Context/src/pages/loginPage.validation.ts
deleted file mode 100644
index ae379b5d..00000000
--- a/hooks/15_Context/src/pages/loginPage.validation.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import {
- createFormValidation,
- ValidationConstraints,
- Validators
-} from "lc-form-validation";
-
-const loginFormValidationConstraints: ValidationConstraints = {
- fields: {
- login: [{ validator: Validators.required }],
- password: [{ validator: Validators.required }]
- }
-};
-
-export const loginFormValidation = createFormValidation(
- loginFormValidationConstraints
-);
diff --git a/hooks/15_Context/src/pages/loginPage.viewmodel.ts b/hooks/15_Context/src/pages/loginPage.viewmodel.ts
deleted file mode 100644
index 4b3cf5b4..00000000
--- a/hooks/15_Context/src/pages/loginPage.viewmodel.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { FieldValidationResult } from "lc-form-validation";
-
-export interface LoginFormErrors {
- login: FieldValidationResult;
- password: FieldValidationResult;
-}
-
-export const createDefaultLoginFormErrors = (): LoginFormErrors => ({
- login: new FieldValidationResult(),
- password: new FieldValidationResult()
-});
diff --git a/hooks/15_Context/src/pages/pageB.tsx b/hooks/15_Context/src/pages/pageB.tsx
index af5c08a3..ccf75980 100644
--- a/hooks/15_Context/src/pages/pageB.tsx
+++ b/hooks/15_Context/src/pages/pageB.tsx
@@ -2,7 +2,7 @@ import * as React from "react";
import { Link } from "react-router-dom";
import { SessionContext } from "../common";
-export const PageB = () => {
+export const PageB: React.FC = () => {
const loginContext = React.useContext(SessionContext);
return (
diff --git a/hooks/15_Context/webpack.config.js b/hooks/15_Context/webpack.config.js
index 31ab3dba..32eff849 100644
--- a/hooks/15_Context/webpack.config.js
+++ b/hooks/15_Context/webpack.config.js
@@ -1,19 +1,19 @@
-var HtmlWebpackPlugin = require("html-webpack-plugin");
-var MiniCssExtractPlugin = require("mini-css-extract-plugin");
-var webpack = require("webpack");
-var path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const webpack = require("webpack");
+const path = require("path");
-var basePath = __dirname;
+const basePath = __dirname;
module.exports = {
context: path.join(basePath, "src"),
resolve: {
- extensions: [".js", ".ts", ".tsx"]
+ extensions: [".js", ".ts", ".tsx"],
},
entry: ["@babel/polyfill", "./index.tsx"],
output: {
path: path.join(basePath, "dist"),
- filename: "bundle.js"
+ filename: "bundle.js",
},
devtool: "source-map",
devServer: {
@@ -21,7 +21,7 @@ module.exports = {
inline: true, // Enable watch and live reload
host: "localhost",
port: 8080,
- stats: "errors-only"
+ stats: "errors-only",
},
module: {
rules: [
@@ -31,32 +31,33 @@ module.exports = {
loader: "awesome-typescript-loader",
options: {
useBabel: true,
- babelCore: "@babel/core" // needed for Babel v7
- }
+ babelCore: "@babel/core", // needed for Babel v7
+ },
},
{
test: /\.css$/,
- use: [MiniCssExtractPlugin.loader, "css-loader"]
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: "file-loader",
options: {
- name: "assets/img/[name].[ext]?[hash]"
- }
- }
- ]
+ name: "assets/img/[name].[ext]?[hash]",
+ esModule: false,
+ },
+ },
+ ],
},
plugins: [
//Generate index.html in /dist => https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: "index.html", //Name of file in ./dist/
template: "index.html", //Name of template in ./src
- hash: true
+ hash: true,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
- chunkFilename: "[id].css"
- })
- ]
+ chunkFilename: "[id].css",
+ }),
+ ],
};
diff --git a/readme.md b/readme.md
index 7625f920..9036f076 100644
--- a/readme.md
+++ b/readme.md
@@ -1,14 +1,16 @@
# React Typescript by sample
+[🇪🇸 Versión Español](./readme_es.md)
+
The goal of this project is to provide a set of simple samples, providing and step by step guide to
-start working with React and Typescript.
+start working with React and TypeScript.
We have incorporated a set of examples based on hooks.
Right now you got two main folders:
- [Hooks](./hooks): set of samples migrated to hooks (right now 15 samples migrated), if you are new to
- React, or you are going to start working on a new project, I recommend you going through these
+ React, or you are going to start working on a new project, We recommend you going through these
examples.
- [Old_class_components_samples](./old_class_components_samples): The old samples, just in case you need to work with older react
@@ -161,7 +163,7 @@ entity passed via props.
### 06 Handling asynchronous calls
Members fake api replaced with async call to api github to retrieve list of
-members of a given team.
+members of a given organization.
### 07 Forms
@@ -186,9 +188,11 @@ we will call async operations and fitting them into Redux architecture.
### 10 SpinnerAsync
-Display a busy indicator when an ajax request is in progress.
+Display a busy indicator while an ajax request is in progress.
-To have a global count of promises gong on we are using [react-promise-tracker](https://github.com/Lemoncode/react-promise-tracker) and to display a cool spinner [react-spinner](https://github.com/davidhu2000/react-spinners)
+To have a global count of promises gong on we are using
+[react-promise-tracker](https://github.com/Lemoncode/react-promise-tracker)
+and to display a cool spinner [react-spinner](https://github.com/davidhu2000/react-spinners)
### 11 Testing reducers
@@ -228,8 +232,10 @@ Add a login page using Material-UI.
# Contributors
-Special thanks to [Jehu Sagardoy](https://github.com/jsagardoy) for his contributions checking
-and getting uptodate examples.
+Thank you very much to the contributors for keeping the project updated in all the examples.
+
+- [Jehu Sagardoy](https://github.com/jsagardoy)
+- [Luis del Amo](https://github.com/delamux)
# About Basefactor + Lemoncode
@@ -239,4 +245,5 @@ We are an innovating team of Javascript experts, passionate about turning your i
[Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
-For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
+For the LATAM/Spanish audience we are running an Online Front End Master degree,
+more info: [http://lemoncode.net/master-frontend](http://lemoncode.net/master-frontend)
diff --git a/readme_es.md b/readme_es.md
new file mode 100644
index 00000000..2107c97f
--- /dev/null
+++ b/readme_es.md
@@ -0,0 +1,246 @@
+# React Typescript - ejemplos
+
+[🇬🇧 English version](./readme.md)
+
+El objetivo de este proyecto es ofrecer un set de ejemplos simples, proporcionando una guía paso a paso
+para empezar a trabajar con React y TypeScript.
+
+Hemos incorporado un set de ejemplos basados en hooks.
+
+Ahora mismo hay dos carpetas principales:
+
+- [Hooks](./hooks): set de ejemplos migrados a hooks (ahora mismo 15 ejemplos), si eres nuevo en React, o vas a empezar a trabajar en
+un nuevo proyecto, Te recomendamos que lo hagas a través de ´estos ejemplos.
+
+- [Old_class_components_samples](./old_class_components_samples): Los ejemplos viejos, sólo en caso de que necesite trabajar en un proyecto de versiones viejas de React o necesites mantener código legacy.
+
+Si quieres sumerjirte más en React Hooks puedes chequear este repo: [React Hooks By Example](https://github.com/Lemoncode/react-hooks-by-example)
+
+Otros repos guiados disponibles (React / Redux + TypeScript):
+
+- [Redux By Sample](https://github.com/Lemoncode/redux-by-sample)
+- [Desde React a Redux](https://github.com/Lemoncode/from-react-to-redux-ts)
+- [Redux Sagas](https://github.com/Lemoncode/redux-sagas-typescript-by-example)
+
+# Ejemplos
+
+Características:
+
+- Bundling basado en webpack.
+- Basado en React + Typescript.
+- Navegación simple usando react-router.
+- Gestionando llamadas asíncronas y actualizaciones.
+- Usando la librería Redux (todavía no está disponible para la version en Hooks, próximamente).
+- Manejando llamadas asíncronas vía Redux-Thunk + Redux Saga (todavía no está disponible para la version en Hooks, próximamente).
+- Añadiendo sporte para test unitarios (todavía no está disponible para la version en Hooks, próximamente).
+- Implementando Lazy Loading (todavía no está disponible para la version en Hooks, próximamente).
+- ...
+
+## Para empezar
+
+1. Instalar [NodeJS](http://www.nodejs.org)
+2. Descarga este repo
+3. Abre la consola de comandos que prefieras y haz 'cd' en el directorio de ejemplo dentro de este repo en tu máquina.
+4. `npm install` - Instalación de los paquetes
+5. `npm start` - Build del proyecto y lanza el servidor web (webpack-dev-server).
+6. Copia y pega esta dirección en tu navegador [http://localhost:8080/](http://localhost:8080/) si ´éste no se abre automáticamente.
+
+# muestras
+
+## Hooks
+
+### [00 Boiler plate](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/00_BoilerPlate)
+
+Bundling + npm start basado en webpack.
+
+### [01 Hello React](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/01_HelloReact)
+
+Muestra el texto 'Hello React'.
+
+Hello world, muestra siemple de React render.
+
+### [02 Propiedades](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/02_Properties)
+
+Muestra el texto 'Hello {name}' (donde nombre es una propiedad que contiene el nombre).
+
+Introducción al concepto básico de React, manejando propiedades.
+
+### [03 State](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/03_State)
+
+Empezando desde la muestra 02, permite que el usuario cambie el nombre que se muestra.
+
+Introducción al concepto básico de React, manejando el Estado (State) usando Hooks.
+
+### [04 Callback](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/04_Callback)
+
+Empezando desde la nuestra 03, permite que el usario cambie la propiedad name
+solo cuando presiona sobre el botón _change_ .
+
+Usando callbacks.
+
+### [05 Refactor](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/05_Refactor)
+
+Refactor de la muestra 04, Limpieza y discusión sobre dónde debe estar el estado.
+
+Reafactorizar el trabajo realizado.
+
+### [06 Enable](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/06_Enable)
+
+Empezando desde la nuestra 05, enable / disable del botón _change_
+cuando el texto está vacío o tiene el mismo nombre que originalmente,.
+
+Componentes Enable/disable
+
+### [07 ColorPicker](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/07_ColorPicker)
+
+demo simple de color picker (muestras cómo funcionan las propiedades).
+
+### [08 ColorPicker Refactor](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/08_ColorPickerRefactor)
+
+ColorPicker refactorizado.
+
+### [09 menú lateral](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/09_Sidebar)
+
+Simple implementación de un menú lateral.
+
+### [10 Tabla Mock](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/10_TableMock)
+
+Renderiza una tabla y usa un componente hijo para renderizar cada fila, usando un mock de datos.
+
+### [11 Tabla Axios](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/11_TableAxios)
+
+Empezando desde la nuestra 10, elimina el mock de datos, utiliza una REST API real (api Github),
+utiliza axios para mejorar el rendimiento de la llamada fetch.
+
+### [12 React Router](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/12_ReactRouter)
+
+Empezando desde la nuestra 03, empezando a utilizar React-Router (navegación SPA).
+
+### [13 Formulario Login](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/13_LoginForm)
+
+Empezando desde la nuestra 12, implementa una página de login básica,
+que redireccione al usuario a otra página cuando el login haya completado satisfactoriamente.
+
+### [14 Validación de formulario](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/14_FormValidation)
+
+Empezando desde la nuestra 13, añadir validaciones al formulario de login.
+
+### [15 Context](https://github.com/Lemoncode/react-typescript-samples/tree/master/hooks/15_Context)
+
+SEmpezando desde la nuestra 14, learn how React 16 context api works.
+SEmpezando desde la nuestra 14, emprender cómo funciona la API de React 16 Context.
+
+## Carpeta class vieja
+
+### 00 Boiler plate
+
+Bundling + npm start basado en webpack.
+
+### 01 Hello React
+
+Hello world, muestra siemple de React render.
+
+### 02 Componentes
+
+Creando una cabecera común y una página 'about' con componentes de React.
+
+### 03 Navegación
+
+Creando una página de "miembros", añadir navegación usando react-router.
+
+### 04 Motrar datos
+
+Crear un componenente de lista de 'sólo lectura' (table >> tr >> td),
+leer una lista de miembros desde una API falsa y añadirlos dentro del componente 'state'
+
+### 05 Prensentación de componentes
+
+Dividir el componente de lista en dos: componente de Lista y Fila,
+pasar la entidad del miembro a través de las 'props' del componente.
+
+### 06 Manejando llamadas asíncronas
+
+Reemplazar la API false con una llamada asíncrona a la API de github y obtener la lista de miembros de una organización.
+
+### 07 Formularios
+
+En esta muestra añadiremos un link en la página de miembros que navegará hacia la 'página del miembro'.
+Esta nueva página mostrará un formulario donde tendrás que introducir la url del avatar, el 'login' y el 'id'
+del nuevo miembro (sólo suponiendo que podemos añadir esa info).
+
+### 08 Parámetros de navegación + Validaciones
+
+Editar un miembro seleccionado, aquí aprenderemos cómo añadir parámetros al link de navegación
+y cómo obtenerlo desde el componente.
+
+La validación realizada hasta ahora:
+
+- Login: requerido, debe ser una cadena de texto (al menos 3 caracteres de longitud).
+
+### 09 Redux
+
+Añadido soporte para Redux, estado aislado en 'Redux reducers', implementa carga, guardar,
+ciclo de validación simple. Esta muestra usa una API falsa, en las siguientes muentras
+haremos llamadas a operaciones asíncronas y las ajustaremos dentro de la arquitectura de Redux.
+
+### 10 SpinnerAsync
+
+Muestra un indicador de carga mientras la petición ajax está en progreso.
+
+Para tener un recuento global de las promesas que estamos usando
+[react-promise-tracker](https://github.com/Lemoncode/react-promise-tracker)
+y mostras un bonito spinner [react-spinner](https://github.com/davidhu2000/react-spinners)
+
+### 11 Testeando reducers
+
+Muestra actualizada usando Jest.
+
+### 12 Testeando acciones
+
+Muestra actualizada usando Jest.
+
+### 13 Testeando componentes (Contenedores y presentaciones)
+
+Pendiente de actualizar Jest + Enzyme
+
+### 14 Reemplazando Redux Thunk con Redux Saga
+
+Pendiente de actualizar
+
+### 15 Lazy Loading y React-Router
+
+Pendiente de actualizar
+
+### 16 Añadir middlewares personalizados
+
+Pendiente de actualizar
+
+### 17 Añadir soporte para ReactHotloader y herramientas ReduxDev
+
+Pendiente de actualizar
+
+### 18 Hooks
+
+Replace class components by stateless components using Hooks.
+
+### 19 Formulario Login
+
+Añadir página de Login usando AMaterial-UI.
+
+# Colaboradores
+
+Muchas gracias a los colaborades por mantener el proyecto actualizado en todos los ejemplos.
+
+- [Jehu Sagardoy](https://github.com/jsagardoy)
+- [Luis del Amo](https://github.com/delamux)
+
+# Sobre Basefactor + Lemoncode
+
+Somos un equipo innovador de expertos en JavaScript, apasionados en convertir tus ideas en productos robustos y consistentes.
+
+[Basefactor, consultoría por Lemoncode](http://www.basefactor.com) proporciona servicios de consultoría y servicios de orientación.
+
+[Lemoncode](http://lemoncode.net/services/en/#en-home) proporciona servicios de formación.
+
+Para la audiencia de LATAM/España estamos llevando a cabo un Master Online Front End, más info:
+[http://lemoncode.net/master-frontend](http://lemoncode.net/master-frontend)