This repository follows various tutorials including the Facebook's official Tutorial (Tic-Tac-Toe).
Instead of using create-react-app, I chose to build it from the ground using Webpack2 and ES6.
Clone and run npm run prod
.
URLs include:
/
-> root page including the Facebook's tic-tac-toe game/todo.html
-> To-Do application
Tic-Tac-Toe React Tutorial: (/
)
To Do Lists React Tutorial: (/todo.html
)
We must understand and decide how the layouts will look like. It is good to break down each components by each functionalities and each block in the layouts. The examlple app below would have 4 components: header, Input field, List of tasks, and managing lists components.
Always break down into smaller pieces depending on the layout!!
Run npm install html-webpack-plugin --save-dev
Configure the webpack.config.js
to include the plugins, output path and filename. The path
must have an absolute path as shown in below.
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.bundle.js'
},
plugins: [new HtmlWebpackPlugin()]
}
Then run npm run dev
, and dynamically generate html file with the app.bundle.js
script.
To use the custom template, add the plugin as shown in the official guide.
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.bundle.js'
},
plugins: [new HtmlWebpackPlugin({
title: 'My Project!', // Project Title
minify: {
collapseWhitespace: true // minifies the html template
},
template: './src/index.ejs', // Load a custom template (ejs by default see the FAQ for details)
})
]
}
<%= htmlWebpackPlugin.options.title %>
in index.ejs will use the webpack.config.js
's project title.
Run npm install css-loader --save-dev
Add the module into the webpack configuration.
const path = require('path')
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.bundle.js'
},
module: {
rules: [
{ test: /\.css$/, loaders: 'css-loader' } // CSS Loader rule
]
},
plugins: [new HtmlWebpackPlugin({
title: 'My Project!!',
minify: {
collapseWhitespace: true
},
template: './src/index.ejs', // Load a custom template (ejs by default see the FAQ for details)
})
]
}
Then link the css into the app.js.
const css = require('./app.js')
<!-- app.js contents here -->
This won't load the css into the newly created index.html, because it is included in the javascript app.bundle.js
.
Therefore, we need to load the style by adding the style-loader
.
npm install style-loader --save-dev
Then add the configuration to webpack.config.js
{ test: /\.css$/, use: ['style-loader' ,'css-loader'] } // added style-loader! infront of previous css-loader
// Version 1 uses loaders: 'style-loader!css-loader`, but 2 uses the above syntax
You can use ExtractTextPlugin()
to extract all styleshets and put into 1 single file.
The difference between webpack -d
and webpack-dev-server
is that webpack development mode is renders and write files in the disk, the server is written in the Memory.
If you run webpack-dev-server
IT DOES NOT produce the bundle.js file in the disk, whicih means no physical copy of the file. If you want to generate the file, build through webpack
.
-
npm install webpack-dev-server
-
Change
"dev"
in npm scripts of package.json towebpack-dev-server
-
Server is now up!! default is 8080
To Create the configuration file, modify webpack.config.js
by adding the devServer: {}
- Some basic configuration in
webpack.config.js
:devServer: { contentBase: path.join(__dirname, 'dist'), compress: true, stats: "errors-only", open: true, port: 8080 },
There are 2 ways to installing React.
-
Create-React-App
-
Manually installing starting from Webpack server (one I'm using)
For adding React to existing app: Guide
-
npm install -D react react-dom
-
Enable ES6 and JSX by installing
Babel
-
Babel compiles ES6 to javascript, so you don't need to worry about browser compatiabilities.
-
npm install -D babel babel-preset-react babel-preset-es2015
-
Enable above presets in
.babelrc
{ "presets": ["es2015", "react"] }
-
-
Add the ReactDOM in index.html
-
Add the Loader to render JavaScript
-
Need the Webpack to test for javascript files to load the
babel-loader
. More informatino on official Babel guide -
npm install --save-dev babel-loader babel-core
-
Configure
webpack.config.js
by adding a new rule.module: { rules: [ { test: /\.css$/, use: ['style-loader' ,'css-loader'] }, // CSS Loader Rule { test: /\.js$/, exclude: /node_modules/, // Exclude node_modules for faster load use: ["babel-loader"] } // JS Loader rule ] },
-
-
npm run dev
, and you will see the created ReactDOM in app.bundle.js
Clear all before we continue building it especially when we go into the production mode.
- Add another script to the package.json.
"scripts": { "dev": "webpack-dev-server", "prod": "npm run clean && webpack -p", "clean": "rimraf ./dist/*" },
npm run clean
-> cleans out all files in the dist folder
What if you want to generate multiple templates, and not keep it in the dist folder?
You can configure it inthe webpack.config.js by adding more HtmlWebpckPlugin.
plugins: [
new HtmlWebpackPlugin({
title: 'My Project!!',
minify: {
collapseWhitespace: true
},
hash: true,
template: './src/index.html', // Load a custom template (ejs by default see the FAQ for details)
}),
new HtmlWebpackPlugin({
title: 'Contact Page',
minify: {
collapseWhitespace: true
},
hash: true,
filename: contact.html,
template: './src/contact.html', // Load a custom template (ejs by default see the FAQ for details)
})
localhost:port/contact.html
is now accessible.
If you want to have multiple bundles, you will need to define multiple entry points. You can group them as however you wanted.
This is usefuly when you need to use separate css, javascript bundler and have multiple templates to work on.
Once the configuration is made, create the corresponding(contact.js) file inside the src folder.
entry: {
app: './src/app.js', // app.bundle.js
contact: './src/contact.js' // app.contact.js
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js' // specify the name for the bundler
},
-
Only include corresponding JavaScript Bundler for each template.
- Add the
excludeChunks
andchunks
option.
- Add the
This allows us to see the changed CSS without refreshing the page. Similar to Live Reload, but smarter.
Add hot: true
to the devServer
in wepack.config.js
Then, add the plugins for React hot module or CSS hot module. Guide can be found in the official Webpack website.
plugins: [
new HtmlWebpackPlugin({
title: 'My Project!!',
minify: {
collapseWhitespace: true
},
hash: true,
excludeChunks: ['contact'],
template: './src/index.html', // Load a custom template (ejs by default see the FAQ for details)
}),
new HtmlWebpackPlugin({
title: 'Contact Page',
minify: {
collapseWhitespace: true
},
hash: true,
filename: 'contact.html',
chunks: ['<contact></contact>'],
template: './src/contact.html', // Load a custom template (ejs by default see the FAQ for details)
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
Controlling plugins for specific envionment.
We can configure this in package.json by adding
"scripts": {
"prod": "npm run clean && NODE_ENV=production webpack -p"
}
We can use NODE_ENV=production
inside the webpack.config.js
file, and check if it is set to true
or false
.
webpack.config.js
const path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin');
var isProduction = process.env.NODE_ENV === 'production'
// Control configuration for different modes
var cssProduction = whatever you need
var cssDevelopment = whatever you desire
var cssConfig = isProduction ? cssProduction : cssDevelopment
// configure true or false values in the webpack.config.js by using the varialble isProduction or !isProduction
How should we include images inside css or HTML? With below plugins()
-
file-loader
-
image-loader
npm install --save-dev file-loader
npm install --save-dev image-loader
Add the rules
for the .png
files
You can use the |
to separate and test for different file types.
The "file-loader?name=[name].[ext]&outputPath=images/"
Lets us to create the outputPath of images/
in the dist
folder when we run in the production mode.
Installing and using the image-loader
will optimize the image sizes, which will make the page loading faster. Below config didn't use this.
Full webpack.config.js rules
:
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader' ,'css-loader']
}, // CSS Loader Rule
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"]
}, // JS Loader rule
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: "file-loader?name=[name].[ext]&outputPath=images/"
}
]
},
More notes on TIL Repository. Links are listed below.
.jsx
expression must have only one parent element.
Very Basic React Component:
const css = require('./app.css')
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => {
return (
<div>
<h1 className="title">Hello React </h1>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
Export module vs export component.
module:
... codes
...
export default App
exporting component
export const App () => {
return (
<div>
<h1>hello</h1>
</div>
)
}
Components can be customized through params called props
. It's like an argument for functions, and we can defined the props
by passing the argument inside App
component, which Greeeting
component takes in the argumnet and renders it.
const Greeting = (props) => {
const {name, age} = props
return(
<div id="greeting">
<p>Welcome to React Tutorial {name}</p>
<p>You are {age} years old!</p>
</div>
)
}
The above `const {name, age} = props;` statement is caleld <strong>DESTRUCTION</strong>, which destructures the props for us to easily use the passed data.
<strong>Prop Types: </strong> We can also define the data Type for the props, which validates the passed data type before passing it to the components.
const App = () => {
return (
<div>
<Headline />
<Greeting name="james" age="30" />
</div>
)
}
React components can have state by setting this.state
in the constructor
, which should be considered priate to the component.
JavaScript classes we need to explicitly call super()
when defining a constructor of a subclass.
When you want to aggregate data from multiple children or to have two child components communicate with each other, move the state upwards so that it lives in the parent component. The parent can then pass the state back down to the children via props, so that the child components are always in sync with each other and with the parent.
IMMUTABILITY!!
Immutability is IMPORTANT!
There are generally two ways for changing data.
-
Mutate the data by directly changing the values of a variable.
-
Replace the data with a new copy of the object that also includes desired changes.
REASONS:
- Easier Undo/Redo and Time Travel
- Avoiding data mutations lets us keep a reference to older versions of the data, and switch between them if we need to. (ex. game stage)
- Tracking Changes
- Determining When to Re-render in React#
- The biggest benefit of immutability in React comes when you build simple pure components.
- Performance, (
shouldComponentUpdate()
)
Usage
// Copy the original array by using .slice()
arr = [0,1,2,3,4]
newArry = arr.slice()
newArry[2] = "a"
console.log(arr) // returns [0,1,2,3,4]
console.log(newArry) // returns [0,1,"a",3,4]
If a component doesn't have constructors and is very simple, just use Functional Component!!
// Instead of this
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => this.props.onClick()} >
{this.props.value}
</button>
);
}
}
// Use this!!
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
When you render a list of items, React always stores some info about each item in the list.
React asks you to specify a key property on each element in a list, a string to differentiate each component from its siblings.
if the items correspond to objects in a database, the database ID is usually a good choice:
<li key={user.id}>{user.name}: {user.taskCount} tasks left</li>
key is a special property that's reserved by React (along with ref
, a more advanced feature). When an element is created, React pulls off the key property and stores the key directly on the returned element. Even though it may look like it is part of props, it cannot be referenced with this.props.key
.
It's strongly recommended that you assign proper keys whenever you build dynamic lists.