React 学习笔记

React 学习笔记

Notes for Front-End Web Development with React Course

Notes for React Documentation

环境配置

Setting up Git and Node

Install Yarn (Optional)

Yarn 是一个类似npm的包管理框架,具体安装步骤请参考此处

Installing create-react-app

安装命令为yarn global add create-react-app

若想要安装特定版本,则使用create-react-app@VERSION_NUMBER

Generating and Serving a React Project

创建新project命令 create-react-app confusion

启动项目命令 yarn start,启动后可在localhost访问web,默认端口为3000

Configuring React Application

Configure project to use reactstrap

Reactstrap 是一个用于集成 Bootstrap 和 React 应用程序的流行库

1
2
3
yarn add bootstrap
yarn add reactstrap
yarn add react-popper

在index.js中导入bootstrap 4

'bootstrap/dist/css/bootstrap.min.css';```
1
2
3
4

在app.js中导入组件

```import { Navbar, NavbarBrand } from 'reactstrap';

核心概念

JSX

JSX是一个 JavaScript 的语法扩展。

  • React DOM 使用 camelCase(小驼峰命名)来定义属性的名称

Element

Elements are the smallest building blocks of React apps.

将一个element 渲染(render)到root DOM节点中,只需传入ReactDOM.render()

1
2
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));

该JSX片段会定位id为root的节点,并将”Hello World”插入其中。

  • React 元素是不可变的

React DOM(补充)

  • element可以是DOM标签,也可以是用户自定义的component

###React Components

React 允许将代码封装成组件(component),然后像插入普通 HTML 标签一样,在网页中插入这个 component。component使用户能够将UI分成多个独立的、可重复使用的片段。

  • 用户定义的component必须大写开头

  • 定义component的两种方式

    • function component

      1
      2
      3
      function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
      }
    • class component

      1
      2
      3
      4
      5
      class Welcome extends React.Component {
      render() {
      return <h1>Hello, {this.props.name}</h1>;
      }
      }

    以上两个组件是等效的。function component接收带有数据的”props”(属性)对象并返回react element。

Render Component

当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。例如,这段代码会在页面上渲染 “Hello, Sara”:

1
2
3
4
5
6
7
8
9
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
  • 设计原则:组件不能修改自身的props

Component 的组合与拆分

组件可以引用其他组件;组件可拆分为更小的组件,避免过度嵌套,并构建可复用的组件库。

State & Lifecycle

State

仅class component可添加局部的state。State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
  • state和props 的更新可能是异步的,因此,避免直接使用this.state + this.props的表达,而应该让setState接收一个函数:

    1
    2
    3
    this.setState((state, props) => ({
    counter: state.counter + props.increment
    }));

    这个箭头函数等同于

    1
    2
    3
    4
    5
    this.setState(function(state, props) {
    return {
    counter: state.counter + props.increment
    };
    });

Lifecycle

上文的clock计时并非每秒更新。class component中可以声明一些特殊的方法来设置(mount)/清除计时器(unmount),当组件挂载或卸载时方法被执行。

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
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

// 当 Clock 的输出被插入到 DOM 中后,React 就会调用 ComponentDidMount() 生命周期方法。
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

componentWillUnmount() {
clearInterval(this.timerID);
}

// 不要直接修改 State,而是应该使用 setState()
tick() {
this.setState({
date: new Date()
});
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

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

The data flows down

除了拥有state的component,其他component都无法访问state,也无法知道一个component是函数组件或class组件。

组件可以将它的state作为props传入子组件。

Handling Events

事件处理函数示例:

1
2
3
<button onClick={activateLasers}>
Activate Lasers
</button>

通过preventDefault来阻止默认行为。如

1
2
3
4
5
6
7
8
9
10
11
12
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}

在constructor中添加listener

1
this.handleClick = this.handleClick.bind(this);

如果觉得使用bind繁琐,可以使用public class fields 或在回调中使用箭头函数,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}

render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}

render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}

建议还是直接bind函数

向事件处理函数传递额外参数

1
2
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

以上两种方式是等价的

Conditional Rendering

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')
);
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
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}

handleLoginClick() {
this.setState({isLoggedIn: true});
}

handleLogoutClick() {
this.setState({isLoggedIn: false});
}

render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}

return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}

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

使用&&

true && expression 返回expression, false && expression 返回false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);

三目运算法

condition ? True : false

1
2
3
4
5
6
7
8
9
10
11
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}