This is the notes taken while learning React.

[TOC]

React

State

setState() async

Source:

https://zh-hans.reactjs.org/docs/state-and-lifecycle.html

http://huziketang.mangojuice.top/books/react/lesson10

React will not change state immediately after setState().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Button extends React.Component {
constructor () {
super()
this.state = { count: 0 }
}

handleClickOnButton () {
console.log(this.state.count)
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 2
})
console.log(this.state.count)
}
//the out put will be 0, 0, but it already shows 2

render () {
return (
<button onClick={this.handleClickOnButton.bind(this)}>
{this.state.count}
</button>
)
}
}

ReactDOM.render(
<Button />,
document.getElementById('root')
);

If we want to use the state changed before, we can use function in setState()

1
2
3
4
5
6
...
handleClickOnButton () {
this.setState(state => ({ count: state.count + 1}))
this.setState(state => ({ count: state.count + 2}))
}
...

Count will increase by 3 every time.

Props Validation

source:

https://www.runoob.com/react/react-props.html

https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html

If we don’t validate props in React 16, ESLint will report errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var title = "runoob";
// var title = 123;
class MyTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}

MyTitle.propTypes = {
title: PropTypes.string
};

ReactDOM.render(
<MyTitle title={title} />,
document.getElementById('example')
);

More types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import PropTypes from 'prop-types';

MyComponent.propTypes = {
// 你可以将属性声明为 JS 原生类型,默认情况下
// 这些属性都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,

// 任何可被渲染的元素(包括数字、字符串、元素或数组)
// (或 Fragment) 也包含这些类型。
optionalNode: PropTypes.node,

// 一个 React 元素。
optionalElement: PropTypes.element,

// 一个 React 元素类型(即,MyComponent)。
optionalElementType: PropTypes.elementType,

// 你也可以声明 prop 为类的实例,这里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),

// 你可以让你的 prop 只能是特定的值,指定它为
// 枚举类型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),

// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),

// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),

// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),

// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),

// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息。
requiredFunc: PropTypes.func.isRequired,

// 任意类型的数据
requiredAny: PropTypes.any.isRequired,

// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},

// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
// 它应该在验证失败时返回一个 Error 对象。
// 验证器将验证数组或对象中的每个值。验证器的前两个参数
// 第一个是数组或对象本身
// 第二个是他们当前的键。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};

Form

https://reactjs.org/docs/forms.html

we can store input value in state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event) {
this.setState({value: event.target.value});
}

handleSubmit(event) {
alert('name submitted: ' + this.state.value);
event.preventDefault();
}

render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}

JSX

Repeat elements

with empty array.map()

Screen Shot 2020-12-11 at 23.58.59

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//this component list the stars
const Rating: FunctionComponent<Props> = ({ value, number, color }) => {
const starNum: number = Math.floor(value);
const starFrac: number = value - starNum;

return (
<div className="rating">
<span>
{Array.apply(null, Array(starNum)).map(i => (
<i style={{ color }} className="fas fa-star" />
))}

<i
style={{ color }}
className={
starFrac >= 0.5 ? 'fas fa-star-half-alt' : 'far fa-star'
}
/>
</span>
</div>
);
};

another example for rendering a list of item:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function App() {

const ll = ["0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"];
const llItems = ll.map((item) => (
<div className="m-t-20" id={`my-progress-box-${item}`} key={item}>
<div className="d-flex no-block align-items-center">
<span>{item}%</span>
</div>
<div className="progress">
<div className={`progress-bar progress-bar-striped ${item >= 40 ? "bg-green" : "bg-red"}`} role="progressbar" style={{width: `${item}%`}} aria-valuenow="10"
aria-valuemin="0" aria-valuemax="100"/>
</div>
</div>));

return (
<div className="App">
<div style={{width: "200px"}}>
{llItems}
</div>
<button onClick={() => clickButton()}>
Save
</button>
</div>
);
}

Condition rendering

Ref:

https://zh-hans.reactjs.org/docs/conditional-rendering.html

https://stackoverflow.com/questions/35762351/correct-way-to-handle-conditional-styling-in-react

#1. Use className tag
1
2
<div className={`progress-bar progress-bar-striped ${item >= 40 ? "bg-green" : "bg-red"}`} role="progressbar" style={{width: `${item}%`}} aria-valuenow="10"
aria-valuemin="0" aria-valuemax="100"/>
#2. Use style tag
1
2
3
4
5
6
7
8
9
style={{
textDecoration: completed ? 'line-through' : 'none'
}}

//or

style={{
textDecoration: completed && 'line-through'
}}
#3. Use function/component
1
2
3
4
5
6
7
8
9
10
11
12
13
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}

ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);

React Router

Package

1
2
npm install react-router-dom
npm install react-router

Basic Structure

Source:

https://reactrouter.com/web/guides/quick-start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>

{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>

withRouter

we can use withRouter to redirect to another page using this.props.history.push('/page');

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

// A simple component that shows the pathname of the current location
class Home extends React.Component {
static propTypes = {
history: PropTypes.object.isRequired
};

handleSubmit(e) {
this.props.inputSize(this.state.size);
e.preventDefault();
this.props.history.push('/game');
console.log(this.state.size);
}

render() {

return (
<form onSubmit={this.handleSubmit}>
<label className="label">
Rows:
<input
className="input"
type="text"
value={this.state.size[0]}
onChange={this.handleRowChange}
/>
</label>
<label className="label">
Columns:
<input
className="input"
type="text"
value={this.state.size[1]}
onChange={this.handleColumnChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
)
}
}

// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
export default connect(mapStateToProps, { inputSize })(withRouter(Home));