diff --git a/03 State/package.json b/03 State/package.json index d0ba60a..9baa89b 100644 --- a/03 State/package.json +++ b/03 State/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "bootstrap": "^4.1.0", + "npm": "^6.0.0", "react": "^16.3.1", "react-dom": "^16.3.1" } diff --git a/_misc/00 state-callback/.babelrc b/_misc/00 state-callback/.babelrc new file mode 100644 index 0000000..03dfd13 --- /dev/null +++ b/_misc/00 state-callback/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [ + [ + "env", + { + "modules": false + } + ] + ] +} diff --git a/_misc/00 state-callback/package.json b/_misc/00 state-callback/package.json new file mode 100644 index 0000000..b8e918c --- /dev/null +++ b/_misc/00 state-callback/package.json @@ -0,0 +1,37 @@ +{ + "name": "sample", + "version": "1.0.0", + "description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --mode development --inline --hot --open", + "build": "webpack --mode development", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "^6.0.106", + "@types/react": "^16.3.8", + "@types/react-dom": "^16.0.5", + "awesome-typescript-loader": "^5.0.0", + "babel-core": "^6.26.0", + "babel-preset-env": "^1.6.1", + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "style-loader": "^0.20.3", + "typescript": "^2.8.1", + "url-loader": "^1.0.1", + "webpack": "^4.5.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" + }, + "dependencies": { + "bootstrap": "^4.1.0", + "npm": "^6.0.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" + } +} diff --git a/_misc/00 state-callback/readme.md b/_misc/00 state-callback/readme.md new file mode 100644 index 0000000..fd381e6 --- /dev/null +++ b/_misc/00 state-callback/readme.md @@ -0,0 +1,145 @@ +# 00 State Call back + +In this sample we are agoing to see the async nature of setState mehtod. + +In this method we could use a callback to ensure we have the change applied. + +We will take as a starting point sample _04 Callback_: + +## Summary steps: + +- Change nameEdit.tsx to add a boolean property to Props and add a checkbox input +- Change app.tsx to add a boolean property to State and a call back to update the input in the control +- Check everything is working properly. + +## Prerequisites + +Install [Node.js and npm](https://nodejs.org) if they are not already installed on your computer. + +> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors. + +## Steps to build it + +- Copy the content from _04 Callback and execute `npm install`. + +- Let's change _nameEdit.tsx_ to modify the Props and render method to add a new control to show the value was changed. + +_./src/nameEdit.tsx_ + +```diff +interface Props { + initialUserName : string; + onNameUpdated : (newName: string) => void; ++ changed: boolean; +} + +... + + public render(){ + return( + <> + + + ++
++ Changed: ++ ++
+"/> + + ); +``` + +- Let's update _app.tsx_ just to change the _constructor_, the _setUsernameState_ method, and add a new method to manage when the change is applied. + +_./src/app.tsx_ + +```diff + +interface State { + userName: string; ++ changed: boolean; +} + +... + + constructor(props: Props) { + super(props); +- this.state = { userName: 'defaultUserName' }; ++ this.state = { userName: 'defaultUserName', changed: false }; + } + + setUsernameState = (newName: string) => { +- this.setState({userName: newName}); ++ this.setState({userName: newName}, this.nameChanged); + } + ++ nameChanged() { ++ this.setState({changed:true}); ++ } + + public render() { + return ( + <> + +- ++ + + ); + } +``` + +- Now we can check that things are still working as expected (nothing broken so far). + + ``` + npm start + ``` + + + + +- In the _app.tsx_ file let's add a function to set the changed _userName_ in the state. + +```diff + import * as React from 'react'; + import {HelloComponent} from './hello'; ++ import {NameEditComponent} from './nameEdit'; + + interface Props { + } + + interface State { + userName : string; + } + + export class App extends React.Component { + constructor(props : Props) { + super(props); + + this.state = {userName: 'defaultUserName'}; + } + ++ setUsernameState = (event) => { ++ this.setState({userName: event.target.value}); ++ } + + public render() { + return ( ++ <> + ++ ++ + ); + } + } +``` + +> Note down the fat arrow class method, this will avoid loosing the _this_ context on the callback + +- Finally let's test the final sample. + + ``` + npm start + ``` diff --git a/_misc/00 state-callback/src/app.tsx b/_misc/00 state-callback/src/app.tsx new file mode 100644 index 0000000..0663e96 --- /dev/null +++ b/_misc/00 state-callback/src/app.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { HelloComponent } from './hello'; +import { NameEditComponent } from './nameEdit'; + +interface Props { +} + +interface State { + userName: string; + changed: boolean; +} + +export class App extends React.Component { + + constructor(props: Props) { + super(props); + + this.state = { userName: 'defaultUserName', changed: false }; + } + + setUsernameState = (newName: string) => { + this.setState({userName: newName}, this.nameChanged); + } + + nameChanged() { + this.setState({changed:true}); + } + + public render() { + return ( + <> + + + + ); + } +} \ No newline at end of file diff --git a/_misc/00 state-callback/src/hello.tsx b/_misc/00 state-callback/src/hello.tsx new file mode 100644 index 0000000..5636921 --- /dev/null +++ b/_misc/00 state-callback/src/hello.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export const HelloComponent = (props: {userName : string}) => { + return ( +

Hello user: {props.userName} !

+ ); +} diff --git a/_misc/00 state-callback/src/index.html b/_misc/00 state-callback/src/index.html new file mode 100644 index 0000000..bf13d1f --- /dev/null +++ b/_misc/00 state-callback/src/index.html @@ -0,0 +1,13 @@ + + + + + + + +
+

Sample app

+
+
+ + \ No newline at end of file diff --git a/_misc/00 state-callback/src/main.tsx b/_misc/00 state-callback/src/main.tsx new file mode 100644 index 0000000..c091cf4 --- /dev/null +++ b/_misc/00 state-callback/src/main.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { App } from './app'; + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/_misc/00 state-callback/src/nameEdit.tsx b/_misc/00 state-callback/src/nameEdit.tsx new file mode 100644 index 0000000..1a70031 --- /dev/null +++ b/_misc/00 state-callback/src/nameEdit.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; + +interface Props { + initialUserName : string; + onNameUpdated : (newName: string) => void; + changed: boolean; +} + +interface State{ + editingName: string; +} + +export class NameEditComponent extends React.Component{ + constructor(props: Props){ + super(props); + this.state = {editingName: this.props.initialUserName}; + } + + onChange = (event) =>{ + this.setState({editingName: event.target.value}); + } + + onNameSubmit = (event: any): any => { + this.props.onNameUpdated(this.state.editingName); + } + + public render(){ + return( + <> + + + +
+ Changed: + +
+ + ); + } +} + diff --git a/_misc/00 state-callback/tsconfig.json b/_misc/00 state-callback/tsconfig.json new file mode 100644 index 0000000..ba8b3b7 --- /dev/null +++ b/_misc/00 state-callback/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "jsx": "react", + "sourceMap": true, + "noLib": false, + "suppressImplicitAnyIndexErrors": true + }, + "compileOnSave": false, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/_misc/00 state-callback/webpack.config.js b/_misc/00 state-callback/webpack.config.js new file mode 100644 index 0000000..afeaf7d --- /dev/null +++ b/_misc/00 state-callback/webpack.config.js @@ -0,0 +1,64 @@ +let path = require('path'); +let HtmlWebpackPlugin = require('html-webpack-plugin'); +let MiniCssExtractPlugin = require('mini-css-extract-plugin'); +let webpack = require('webpack'); + +let basePath = __dirname; + +module.exports = { + context: path.join(basePath, "src"), + resolve: { + extensions: ['.js', '.ts', '.tsx'] + }, + entry: [ + './main.tsx', + '../node_modules/bootstrap/dist/css/bootstrap.css' + ], + output: { + path: path.join(basePath, 'dist'), + filename: 'bundle.js', + }, + devtool: 'source-map', + devServer: { + contentBase: './dist', // Content base + inline: true, // Enable watch and live reload + host: 'localhost', + port: 8080, + stats: 'errors-only' + }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + loader: 'awesome-typescript-loader', + options: { + useBabel: true, + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: 'assets/img/[name].[ext]?[hash]' + } + }, + ], + }, + 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, + }), + new MiniCssExtractPlugin({ + filename: "[name].css", + chunkFilename: "[id].css" + }), + ], +}; \ No newline at end of file