Notes of React

React ref

1st Create React File

  1. Online
  2. Browser
  3. create-react-app github

npx is a command of npm which is a tool to manage packages in node.js

1
2
3
4
5
npx create-react-app react-store 

cd react-store`

npm start` or `yarn start

localhost:3000

2nd Open folder in VScode

package: dependencies // environment configuration

node_modules: dependencies

public: static resources

3rd Start to Write React Program

1. Prepare

delete original files under src and public

create index.html under public and index.js under src

2. Write your first JS component in index.js

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import ReactDom from 'react-dom';

class Login extends React.Component {
render() {
return <p>Login Component</p>;
}
}

ReactDom.render(<Login />, document.getElementById('root')); // render current component
export default Login; // always add this to the end of the file if it is not in the same folder as index.js

3. JSX with HTML

With JSX, we could return HTML element directly.

1
2
3
4
5
render() {
return (
<p>example</p> //only one element
);
}

It actually tranforms the following format.

React.createElement('p', {classname: 'Login'})

4. Emmet Settings

Emmet is an extension that can help us write HTML component fast. We can edit its settings in preferences->settings->Extensions-> Emmet.

Enable trigger on tab first. Then add javascript as a new key and javascriptreact as a value accordingly in the included languages. In this way, it would help us fill the html element we want to return once you press tab.

emmet1

Also, shortcut for prettier is option + shift + f on Mac.

While for windows, it is alt + shift + f.

5. Return the element we want

We can only return one element each time with JSX. And we need to add a bracket when we use render() { return (<p>abc</p>); } like this.

If we want to return multiple elements, we can use React.Fragment.

1
2
3
4
5
6
7
8
9
10
render() {
return (
const a = 'JS code here';
<React.Fragment>
{a} {/* This would show 'JS code here' */}
<p>abc</p>
<p>ttt</p>
</React.Fragment>
);
}

In JSX, comment is like {/* comment */}and JS code should be like {js code}.

6. Use Bulma template to beautify our component

Bulma is light-weighted and 100%responsive.

We can copy the login template there. Docs->Forms->general

Two ways to import Bulma:

  1. in index.html with CDN

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">

  2. VScode command

1
2
3
yarn add bulma
yarn add node-sass // incompatible with M1 or other version
npm install node-sass@npm:sass // this one works

Create a new folder named css under src. Add scss file. Scss is a grammar of sass (based on css).

import bulma in scss file.

@import '../../node_modules/bulma/bulma.sass';

Copy some css style from github. We can change the color of buttons and columns based on bulma website. Customize -> color

Customized scss file and then import "../css/app.scss"; in Login.js.

IF WE WANT TO USE SCSS FILE IN ALL FILES LATER, IMPORT THEM IN INDEX.JS.

1
2
import "./css/app.scss";
import "./css/style.scss";

Copy style and app scss files from GitHub.

7. Create App.js with Header and Products

Write App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import Header from './Header';
import Products from './Products';

class App extends React.Component {
render() {
return (
<div className="main">
<Header></Header>
<Products></Products>
</div>
);
}
}

export default App;

Write Header and Products templates with css earlier defined.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React from 'react';


class Header extends React.Component {
render() {
return (
<div className="header">
<div className="grid">
<div className="start">
<a href="/">Home</a>
</div>
<div className="end">
<a href="/">Login</a>
<a href="/">Register</a>
</div>
</div>
</div>
);
}
}

export default Header;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';


class Products extends React.Component {
render() {
return (
<div className="products">
<p>Products</p>
</div>
);
}
}

export default Products;

8. Change path to absolute path

In root menu, create a new file named jsconfig.json.

1
2
3
4
5
6
7
{
"compilerOptions": {
"baseUrl": "src"
},

"include": ["src"]
}

In App.js, change import statements.

1
2
3
import React from 'react';
import Header from 'components/Header';
import Products from 'components/Products';

9. Use props to pass parameters

index:<header nickname="Admin" />

header: <span.nickname {this.props.nickname} />

Use renderLink() to choose which element to show.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
renderLink() {
const nickname = this.props.nickname;
if (nickname) {
return (
<span className="nickname">{this.props.nickname}</span>
)
} else {
return (
<React.Fragment>
<a href="/">Login</a>
<a href="/">Register</a>
</React.Fragment>
)
}
}

Import fontawesome in app.sass to use icons.

10. function component

const Header => props => ( return内容 html)

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
import React from "react";

const Header = props => (
<div className="header">
<div className="grid">
<div className="start">
<a href="/">Home</a>
</div>
<div className="end">
{props.nickname ? (
<span className="nickname">
<i className="fas fa-user"></i>
{props.nickname}
</span>
) : (
<React.Fragment>
<a href="/">Login</a>
<a href="/">Register</a>
</React.Fragment>
)}
</div>
</div>
</div>
);

export default Header;

11. Use React Router to navigate

安装react router yarn add react-router-dom

Create Router.js under src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "components/App";
import Login from "components/Login";
import NotFound from "components/NotFound";

const Router = () => (
<BrowserRouter>
<Routes>
<Route path="/" element={ <App />}></Route>
<Route path="/login" element={<Login />}></Route>
<Route path="*" element={<NotFound />}></Route>
</Routes>
</BrowserRouter>
);

export default Router;

change render in index.js

ReactDom.render(<Router />, document.getElementById("root"));

12. Event listener In React

In HTML, we use onclike=”string” to deal with

In React, it’s simiar but with js

微信截图_20211119165905

onClick大写

13. Bind this

In some built-in functions like render() in React, we can use this to get the attributes we want directly. But in our self-defined functions, we need to bind this to get the object we want.

  1. bind(this) with onClick

<a href="/login" className="button" onClick={this.handleClick.bind(this)}> click this </a>

  1. Or use arrow function, 但是容易导致子组件重新渲染

微信截图_20211119231659

  1. with constructor

微信截图_20211119231943

  1. use arrow function to bind directly

微信截图_20211119231959

pass parameters

Inked微信截图_20211119232516_LI

14. Deal with form data

first create ref, and then bind in input, get value by current.value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  emailRef = React.createRef();
passwordRef = React.createRef();

handleSubmit = event => {
// 1. 阻止默认实践行为
event.preventDefault();
// 2. 获取表单数据
const formData = {
email : this.emailRef.current.value, // 在底下input进行bind
password: this.passwordRef.current.value
};
console.log(formData);
// 3. 处理登录逻辑
// 4. 跳转到首页视图
this.props.history.push('/');
}

15. State

We only need to update state to update the whle page.

constructor() should add super() in it. super(props) refers to the parent class constructor. Link text Here

Because constructors of all components extending React.Component will need super() to be initialized. We use this.propsto pass data among parent component and children components. props is a parameter that is used by parent component to pass data to children components.

Define a state:

1
2
3
4
5
6
7
8
9
10
11
12
state = {
isLike: false
};

handleClick = () => {
this.setState({
isLike: !this.state.isLike
});
};


<button className="button" onClick={this.handleClick}> {this.state.isLike ? 'No' : 'Yes'}</button>

If we want to use the last updated state, we need a Callback

1
2
3
this.setState(prevState => {
return {count : prevState.count + 2};
})

16. Controlled Component and Uncontrolled Component

uncontrolled component: can only read the state

controlled component: can change the state of the component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
state = {
email : '',
password : ''
};

handleChange = e => {
console.log(e.target.value); // get the value
console.log(e.target.name); // can deal with two names
this.setState({
[e.target.name]: e.target.value.toUpperCase()
});
};


<input value={this.state.email} onChange={this.handleChange} name="email" /> // the name of input should be the same as the state
<input value={this.state.password} onChange={this.handleChange} name="password" /> // the name of input should be the same as the state

Through this, we can get the form data (email and password).

17. Component Layout

divide the page into two parts, toolbox and products.

create Toolbox.js and Product.js

SCSS could use Nested

18. get the attribute of each product

add attributes in products.js

1
2
3
4
5
6
7
product= {
name: 'Air Jordon 4',
image: 'images/1.jpg',
tags: '45 colors',
price : 59400,
status: 'available'
};

Get those attributes in product.js

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
formatPrice = cents => {
return (cents / 100).toLocaleString('zh', {
style: 'currency',
currency:'CAD',
});
}

render() {
const {name, image, tags, price} = this.props.product;
return (
<div className="product">
<div className="p-content">
<div className="img-wrapper">
<figure className="image is-4by3">
<img src={image} alt={name} />
</figure>
<p className="p-tags">{tags}</p>
<p className="p-name">{name}</p>
</div>
</div>
<div className="p-footer">
<p className="price">{this.formatPrice(price)}</p>
<button className="add-cart">
<i className="fas fa-shopping-cart"></i>
<i className="fas fa-exclamation"></i>
</button>
</div>
</div>
);
}
}

if we move formatPrice to another file, change <p className="price">{this.formatPrice(price)}</p> to <p className="price">{formatPrice(price)}</p>

And use export in that file.

1
2
3
4
5
6
export const formatPrice = cents => {
return (cents / 100).toLocaleString('zh', {
style: 'currency',
currency:'CAD',
});
}

19. set unavailable products

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
render() {
const {name, image, tags, price, status} = this.props.product;
const _pClass = {
available: 'product',
unavailable: 'product out-stock'
}

return (
<div className={_pClass[status]}>
<div className="p-content">
<div className="img-wrapper">
<div className="out-stock-text">Out Of Stock</div>
<figure className="image is-4by3">
<img src={image} alt={name} />
</figure>
</div>
<p className="p-tags">{tags}</p>
<p className="p-name">{name}</p>
</div>
<div className="p-footer">
<p className="price">{formatPrice(price)}</p>
<button className="add-cart" disabled={status === 'unavailable'}>
<i className="fas fa-shopping-cart"></i>
<i className="fas fa-exclamation"></i>
</button>
</div>
</div>
);
}
}