- indecision-app
- NOTE
- SCSS
- Test using Jest
- Other Links
JSX expression
can only have 1 root element, the best pratice is to wrap all elements inside<div>
tag
let template = (
<div>
<h1>title<h1>
<p>description</p>
</div>
);
JSX expression
would ignore/not renderboolean
,undefined
andnull
value.
But
JSX
support others literal valuestring
,number
and even supportsarray
.
JSX
can renderJSX
, nestedJSX
- Logical
AND
operator
let age = 18;
return true && "something"; // will return "something"
// We can use this `and` logical operator to return jsx expression
return age >= 17 && <p>{age} years old</p>; // will return <p>18 years old</p>
The difference between var
, const
, let
var |
const |
let |
|
---|---|---|---|
Function Scope | Y | Y | Y |
Block Scope | N | Y | Y |
Re-assign | Y | N | Y |
Re-define/Re-declare | Y | N | N |
let letName = 'a';
let letName = 'a1'; // error re-define `let`
const constName = 'b';
const constName = 'b1'; // error re-define `const`
var varName = 'c';
var varName = 'c1';
Re-assign means change the current value to a new whole value.
let letName = 'a';
letName = 'a1';
const constName = 'b';
constName = 'b1'; // error re-assign `const`
var varName = 'c';
varName = 'c1';
const sampleObject = {y:2};
const sampleObject.y = 3; // this is working, {y:3}
sampleObject = {}; // error re-assign change a new whole value
function getName()
{
let letName = 'a';
const constName = 'b';
var varName = 'c';
}
console.log(varName); // error access, out of `function scope`
console.log(letName); // error access, out of `function scope`
console.log(constName); // error access, out of `function scope`
Block Scope
means the variable bound to code block
, it can be for
loop, or if statement with the curly bracket {}
.
if (true) {
let letName = 'a';
const constName = 'b';
var varName = 'c';
}
console.log(varName); // output 'a'
console.log(letName); // error access, out of `if statement scope`
console.log(constName); // error access, out of `if statement scope`
Tips :
Better use
const
variable at the first, then change it tolet
if the variable need re-assign.
// es5 example function with name `getFirstName`
function getFirstName(fullname) {
return fullname.split(' ')[0];
}
// still es5 example function `anonymous`, instead we stored this function to variable `getFirstName`
var getFirstName = function (fullname) {
return fullname.split(' ')[0];
}
// es6 example function, remove the `function keyword`
const getFirstName = (fullname) => {
return fullname.split(' ')[0];
}
// es6 example function, can make to even simpler format as 1 line script, remove `return keyword`.
const getFirstName = (fullname) => fullname.split(' ')[0];
In es5 we can access arguments
object
function greet(name) {
console.log(arguments); // arguments object : arguments[0] 'patrik', arguments[1] 'spongebob'
return `Hello ${name}`;
}
console.log(greet('patrik', 'spongebob')); // echo Hello patrik
const greet = (name) => {
console.log(arguments); // Uncaught ReferenceError: arguments is not defined
return `Hello ${name}`;
}}
Lexical Scoping just means that it uses
this
from the code that contains the Arrow Function.
const multiplier = {
numbers: [1,2,3,4,5],
multiplyBy: 3,
multiply() {
// lexical `this` refer to object `multiplier` even inside the `map` function which is used arrow function, `this` not bound to arrow function.
return this.numbers.map((number) => number * this.multiplyBy);
}
};
console.log(multiplier.multiply());
Tips : in object multiplier
, see that multiply
function without function
keyword.
const multiplier = {
function : multiply() {}
}
// equal to
const multiplier = {
multiply() {}
}
JSX
does not have built in data binding. But React has React Component State to handled this matter. When the state changed then the React Component will recall therender
method. So it will re-render the JSX .
let count = 0;
const addOne = () => {
count++;
console.log('add one', count);
};
// when button clicked count in `h1` element does not changed
// this example does not used react component, only JSX
const template2 = (
<div>
<h1>Count:{count}</h1>
<button onClick={addOne}>+1</button>
</div>
);
ReactDOM.render(template2, placeholder);
How to make the count changed, by manual binding
let count = 0;
const addOne = () => {
count++;
console.log('add one', count);
renderCounterApp(); // call this to update the DOM
};
const renderCounterApp = () => {
const template2 = (
<div>
<h1>Count:{count}</h1>
<button onClick={addOne}>+1</button>
</div>
);
ReactDOM.render(template2, placeholder);
}
// first render count equal to `0`
// because we don't used react component with state, every change in JSX need to be refrected by : recall `ReactDOM.render`.
// With react component using state, this re-render feature is automatically handled
renderCounterApp();
const options = [];
// use spread syntax to push new item to options array
options = [...options, 'item1', 'item2'];
const optionsTemplate = (
<ol>
{options.map((option, index) => <li key={index}>{option}</li>)}
</ol>
);
// <ol><li>item1</li><li>item2</li></ol>
react component is (a
es6 class
that extendsReact.Component
class, and definerender
method) OR (stateless function component
)
class Header extends React.Component {
render() {
return (
<div>
<h1>Header</h1>
<SubHeader />
</div>
);
}
}
// example for `nested` component
class SubHeader extends React.Component {
render() {
return (
<h2>Sub Header</h2>
);
}
}
How to use that Header
component
ReactDOM.render(<Header />, document.getElementById('container-id')); // <div id="container-id"><div><h1>Header</h1><h2>Sub Header</h2></div></div>
We can pass data to react component class, using props
attribute. props
is an object which has key-value pair for each index. props
values can be an object
, array
, literal value : string, int
, or even a function
.
props
is immutable.
// How to pass the data to `props` attribute
class Header extends React.Component {
render() {
const subheader = 'This is the Sub Header text';
return (
<div>
<h1>Header</h1>
<SubHeader subheader={subheader}/>
</div>
);
}
}
// How to retrieve the data from `props` attribute
class SubHeader extends React.Component {
render() {
return (
<h2>{this.props.subheader}</h2>
);
}
}
ReactDOM.render(<Header />, document.getElementById('container-id'));
// <div id="container-id"><div><h1>Header</h1><h2>This is the Sub Header text</h2></div></div>
key
keyword can not be used as theprop
name. Because React used it as identifier for unique childs elements. To avoid this warningWarning: Each child in an array or iterator should have a unique "key" prop.
class Options extends React.Component {
render() {
return (
const options = ['item1'];
<div>
<p>Here are your options</p>
<ol>
{options.length && options.map((option, index) => <Option key={index} option={option}/>)}
</ol>
</div>
);
}
}
class Option extends React.Component {
render() {
return (
console.log(this.props); // {options: 'item1'}, there is no `key` index name in props object.
<li>{this.props.option}</li>
);
}
}
Because props can use
function
as a value, for nested React Component, the child can communicate with the parent component function, if that function pass as the prop attribute to the child component.
class ParentComponent extends React.Component {
parentFunction() {
alert();
}
render() {
return (
<div>
<ChildComponent parentFunction={this.parentFunction}/>
</div>
);
}
}
class ChildComponent extends React.Component {
render() {
return (
<div>
<button onClick={this.props.parentFunction}>Click Here</button>
</div>
);
}
}
React component can has defaultProps
. It's usefull to set default value for rendering
or state
.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: props.count
};
}
render() {
return (
<div>
<h1>Count:{this.state.count}</h1>
</div>
);
}
}
Counter.defaultProps = {
count: 0
}
ReactDOM.render(<Counter count={10}/>, document.getElementById('#container'));
// <div id="container"><div><h1>10</h1></div></div>
// With defaultProps we can set the state `count` dynamically.
There is a case when we trying to access this
but get error access of undefined
or null
. Because We trying to access this
out of scope. That's why we need to manually bind
the this
.
const Person = {
name: 'Spongebob',
getName: function() {
return this.name;
}
};
console.log(Person.getName());
const getName = Person.getName;
console.log(getName()); // Uncaught TypeError: Cannot read property 'name' of undefined
const getNameWithBind = Person.getName.bind(Person);
console.log(getNameWithBind()); // Spongebob
// or we can even set the binding to another object
const getNameWithBind2 = Person.getName.bind({name: 'Patrick'});
console.log(getNameWithBind()); // Patrick
The same case happened for React Component class. When we try to access this
from another function beside render
.
class Options extends React.Component {
constructor(props) {
super(props);
this.handleRemoveAll = this.handleRemoveAll.bind(this); // binding `this` to method handleRemoveAll.
}
handleRemoveAll() {
console.log(this.props); // So we can access `this.props` object attribute
}
render() {
return (
<div>
<button onClick={this.handleRemoveAll}>Remove All</button>
</div>
);
}
}
To avoid this manual
bind
thethis
. We can use babel plugin@babel/plugin-proposal-class-properties
(https://babeljs.io/docs/en/babel-plugin-proposal-class-properties) which enabledclass
to have functions declared using arrow function.
Code example : sample counter with react component and state
- pass
object
as parameter tosetState
function AVOID THIS
class Counter extends React.Component {
constructor(props) {
super(props);
this.reset = this.reset.bind(this);
this.state = {
count: 15
};
}
// when this reset method is called count set to 16, not as expected, should be 1
reset() {
this.setState({
count: 0
});
this.setState({
count: ++this.state.count
});
}
render() {
return (
<div>
<button onClick={this.reset}>reset</button>
</div>
);
}
}
That's happened cause react run the setState
in a-sync way. So in the 2nd called of setState
, it still got the original
value 15
not the prevState
0
. By using parameter function
, we can get this prevState
.
Do not change
prevState
, it should be immutable, because it can affect the lyfe cycle method likecomponentDidUpdate
. We want to use the actualprevState
, not theprevState
that we accidentally changed.
- pass
function
as parameter tosetState
function PREFERRED
class Counter extends React.Component {
constructor(props) {
super(props);
this.reset = this.reset.bind(this);
this.state = {
count: 15
};
}
// when this reset method is called count set to 1, as expected
reset() {
this.setState(() => {
return {
count: 0
};
});
// ++prevState.count is not recomended because it changes the prevState.count
this.setState((prevState) => {
return {
count: prevState.count + 1
};
});
}
render() {
return (
<div>
<button onClick={this.reset}>reset</button>
</div>
);
}
}
Simple react component in form of function, but does not have any state/stateless
. Usefull for react component which only needed to render, without has any logic related to state
.
But this stateless function still can have props
.
// sample with explicit return
const User = (props) => {
return (
<div>
<p>name: {props.name}</p>
<p>age: {props.age}</p>
</div>
);
}
ReactDOM.render(<User name="Spongebob" age={17}>, document.getElementById('container'));
//<div id="container"><div><p>name: Spongebob</p><p>age: 17</p></div></div>
Sample with implicit return JSX
const User = (props) => (
<div>
<p>name: {props.name}</p>
<p>age: {props.age}</p>
</div>
);
use component built in
props.children
const Layout = (props) => {
return (
<div>
<h1>Header</h1>
{props.children}
<p>Footer</p>
</div>
);
};
ReactDOM.render(<Layout><p>inline text</p></Layout>, document.getElementById('container'));
// <div id="container"><div><h1>Header</h1><p>inline text</p><p>Footer</p></div></div>
const getUser = () => {
return {
'username': 'spongebob'
};
}
// simplify to this
const getUser = () => ({'username': 'spongebob'});
export const add = (num1, num2) => num1 + num2;
export const multiply = (num1, num2) => num1 * num2;
OR
const add = (num1, num2) => num1 + num2;
const multiply = (num1, num2) => num1 * num2;
export {add, multiply};
How to import that
import {add, multiply} from './FILE/PATH.js';
- we don't have to reference by
name
.
const add = (num1, num2) => num1 + num2;
const multiply = (num1, num2) => num1 * num2;
const subtract = (num1, num2) => num1 - num2;
export {add, multiply, subtract as default};
OR
export const add = (num1, num2) => num1 + num2;
export const multiply = (num1, num2) => num1 * num2;
const subtract = (num1, num2) => num1 - num2;
export default subtract;
import
import defaultSubtract, {add, multiply} from './FILE/PATH.js';
// we can change the name, because it always be refered to default.
defaultSubtract(5, 2); // output 3
- https://reactjs.org/docs/dom-elements.html
- https://reactjs.org/docs/events.html#form-events
- react developer tool - chrome extension
- https://reactjs.org/docs/state-and-lifecycle.html
- https://webpack.js.org/configuration/
- https://nodejs.org/api/path.html
- https://babeljs.io/docs/en/babel-preset-latest
- https://webpack.js.org/configuration/devtool/
- https://webpack.js.org/configuration/dev-server/
- https://www.npmjs.com/package/babel-plugin-transform-class-properties
- https://github.com/reactjs/react-modal#examples
- https://sass-lang.com/guide
- https://www.npmjs.com/package/package
- http://getbem.com/
- https://necolas.github.io/normalize.css/
- http://sass-lang.com/documentation/Sass/Script/Functions.html