我是靠谱客的博主 炙热毛巾,这篇文章主要介绍React 官网核心概念核心概念,现在分享给大家,希望可以做个参考。

核心概念

2. JSX

JS 扩展

复制代码
1
2
const element = <h1>Hello, world!</h1>;

视图与逻辑之间的结合


Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用

以下两种示例代码完全等效:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const element = ( <h1 className="greeting"> Hello, world! </h1> ); // 预先执行一些检查,以帮助你编写无错代码 const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' ); // 实际上它创建了一个这样的对象 // 注意:这是简化过的结构 const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };

这些对象被称为 “React 元素”

它们描述了你希望在屏幕上看到的内容 React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新

3. 元素渲染进根节点

元素:构成 React 应用的最小砖块 ,描述了你在屏幕上想看到的内容

将一个元素渲染为 DOM

假设你的 HTML 文件某处有一个 <div>

复制代码
1
2
<div id="root"></div>

想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()

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

4. 组件 & Props

组件,类似函数, 接受 “props” = > React 元素 (展页面示内容)

定义组件

1️⃣ 编写 JavaScript 函数

复制代码
1
2
3
4
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }

2️⃣ 使用 ES6 的 class 来定义组件

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

组件名称必须以大写字母开头

不能更改props

不能修改自身的 props。来看下这个 sum 函数:

复制代码
1
2
3
4
5
function sum(a, b) { // 纯函数:不会尝试更改入参 return a + b; }
复制代码
1
2
3
4
5
function withdraw(account, amount) { // 不是纯函数:更改了自己的入参 account.total -= amount; }

所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

5. State & 生命周期

state : 私有的,完全受控于当前组件。 是组件自动更新

将函数组件转换成 class 组件

缺点:需要将 ReactDOM.render 放进一个函数,然后使用 setInterval 调用,并且 Clock需要传入参数

改进:直接调用 ReactDOM.render 并且 Clock 不要参数

方法:将函数组件变为 class 组件,state 类似与 props

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000);
  1. 创建一个同名的 ES6 class,并且继承于 React.Component。
  2. 添加一个空的 render() 方法。
  3. 将函数体移动到 render() 方法之中。
  4. render() 方法中使用 this.props 替换 props
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1 class Clock extends React.Component { // 2 render() { // 3 return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); // 4 this.props } }

向 class 组件中添加局部的 state

我们通过以下三步将 date 从 props 移动到 state 中:

  1. render() 方法中的 this.props.date 替换成 this.state.date
  2. 添加一个 class 构造函数,然后在该函数中为 this.state 赋初值
  3. 移除 <Clock /> 元素中的 date 属性
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Clock extends React.Component { constructor(props) { // 将 props 传递到父类的构造函数中 // Class 组件应该始终使用 props 参数来调用父类的构造函数 super(props); this.state = {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') );

将生命周期方法添加到 Class 中

在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。

复制代码
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
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } // 当 Clock 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)” componentDidMount() { // 把计时器的 ID 保存在 this.timerID 之中 // 尽管 this.props 和 this.state 是 React 本身设置的,且都拥有特殊的含义,但是其实你可以向 class 中随意添加不参与数据流(比如计时器 ID)的额外字段 this.timerID = setInterval( () => this.tick(), 1000 ); } // 当 DOM 中 Clock 组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)” componentWillUnmount() { // 清除计时器 clearInterval(this.timerID); } // Clock 组件每秒都会调用它 tick() { // 使用 this.setState() 来时刻更新组件 state 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') );

正确地使用 State

关于 setState() 你应该了解三件事:

不要直接修改 State

例如,此代码不会重新渲染组件:

复制代码
1
2
3
// Wrong this.state.comment = 'Hello';

而是应该使用 setState():

复制代码
1
2
3
// Correct this.setState({comment: 'Hello'});

构造函数是唯一可以给 this.state 赋值的地方:

State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

因为 this.propsthis.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

例如,此代码可能会无法更新计数器:

复制代码
1
2
3
4
5
// Wrong this.setState({ counter: this.state.counter + this.props.increment, });

要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:

复制代码
1
2
3
4
5
// Correct this.setState((state, props) => ({ counter: state.counter + props.increment }));

上面使用了箭头函数,不过使用普通的函数也同样可以:

复制代码
1
2
3
4
5
6
7
// Correct this.setState(function(state, props) { return { counter: state.counter + props.increment }; });

State 的更新会被合并

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

例如,你的 state 包含几个独立的变量:

复制代码
1
2
3
4
5
6
7
8
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }

然后你可以分别调用 setState() 来单独地更新它们:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }

这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments

6. 事件处理

在 React 中,你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。例如,传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写:

复制代码
1
2
3
4
<a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a>

在 React 中,可能是这样的:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ); }

在这里,e 是一个合成事件

使用 React 时,你一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器

事实上,你只需要在该元素初始渲染的时候添加监听器即可

复制代码
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
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // 为了在回调中使用 `this`,这个绑定是必不可少的 // 在 JavaScript 中,class 的方法默认不会绑定 this。 this.handleClick = this.handleClick.bind(this); } // 当你使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法 handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') );

不使用 bind 的另一种方法,可以在回调中使用箭头函数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 return ( <button onClick={() => this.handleClick()}> Click me </button> ); } }

向事件处理程序传递参数

在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 id 是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:

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

7. 条件渲染

登陆退出改变文字

复制代码
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
import React from 'react' import ReactDOM from 'react-dom' // 两种行为 function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; } // 呈现两种行为的方式 function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } // 两个 button 样式 function LoginButton(props) { return ( <button onClick={props.onClick}> Login </button> ); } function LogoutButton(props) { return ( <button onClick={props.onClick}> Logout </button> ); } 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; // isLoggedIn 决定 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') );

点击隐藏 Warning

复制代码
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
import React from 'react' import ReactDOM from 'react-dom' // 传入的状态决定是否展示内容到界面上 function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning </div> ); } class Page extends React.Component { constructor(props) { super(props); this.state = { showWarning: true }; this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); } render() { return ( <div> <WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') );

8. 列表 & Key

复制代码
1
2
3
4
5
6
7
8
9
10
import React from 'react' import ReactDOM from 'react-dom' const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li key={number}>{number}</li>); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') );
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react' import ReactDOM from 'react-dom' function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );

key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

元素的 key 只有放在就近的数组上下文中才有意义。

比方说,如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

复制代码
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 ListItem(props) { // 正确!这里不需要指定 key: return <li>{props.value}</li>; } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 正确!key 应该在数组的上下文中被指定 <ListItem key={number.toString()} value={number} /> ); return ( <ul> {listItems} </ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') );

10. 状态提升

计算水在给定温度下是否会沸腾

复制代码
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
function BoilingVerdict(props) { if (props.celsius >= 100) { return <p>The water would boil.</p>; } return <p>The water would not boil.</p>; } class Calculator extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { temperature: '' }; } handleChange(e) { this.setState({ temperature: e.target.value }); } render() { const temperature = this.state.temperature; return ( // fieldset:对表单进行分组,一个表单可以有多个fieldset // legend:说明每组的内容描述 <fieldset> <legend>Enter temperature in Celsius:</legend> {/* value: 显示的值为 temperature */} <input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } }

两个输入框内的数值彼此能够同步

状态提升:将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 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
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import React from 'react' import ReactDOM from 'react-dom' function BoilingVerdict(props) { if (props.celsius >= 100) { return <p>The water would boil.</p>; } return <p>The water would not boil.</p>; } const scaleNames = { c: 'Celsius', f: 'Fahrenheit' }; class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { temperature: '' }; } handleChange(e) { // Before: this.setState({temperature: e.target.value}); this.props.onTemperatureChange(e.target.value); } render() { // Before: const temperature = this.state.temperature; // 自定义组件中的 temperature 和 onTemperatureChange 这两个 prop 的命名没有任何特殊含义。 // 我们可以给它们取其它任意的名字,例如,把它们命名为 value 和 onChange 就是一种习惯。 const temperature = this.props.temperature; const scale = this.props.scale; return ( <fieldset> <legend>Enter temperature in {scaleNames[scale]}:</legend> <input value={temperature} // handlechange 在每次按键时都会执行并更新 React 的 props onChange={this.handleChange} /> </fieldset> ); } } function toCelsius(fahrenheit) { return (fahrenheit - 32) * 5 / 9; } function toFahrenheit(celsius) { return (celsius * 9 / 5) + 32; } function tryConvert(temperature, convert) { const input = parseFloat(temperature); if (Number.isNaN(input)) { return ''; } const output = convert(input); // Math.round()函数: 返回一个数字四舍五入后最接近的整数 const rounded = Math.round(output * 1000) / 1000; return rounded.toString(); } class Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); this.state = {temperature: '', scale: 'c'}; } handleCelsiusChange(temperature) { // 调用 this.setState() 进而请求 React 重新渲染自己本身。 this.setState({scale: 'c', temperature}); } handleFahrenheitChange(temperature) { this.setState({scale: 'f', temperature}); } render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature; const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature; return ( <div> <TemperatureInput scale="c" // 父组件 Calculator 提供参数 temperature={celsius} // 无论哪个输入框被编辑都会调用 Calculator 组件中对应的方法。 然后会调用 this.setState() onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } } ReactDOM.render( <div> <Calculator/> </div>, document.getElementById('root') );

11. 组合 vs 继承

包含关系

使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); } function WelcomeDialog() { return ( <FancyBorder color="blue"> <h1 className="Dialog-title"> Welcome </h1> <p className="Dialog-message"> Thank you for visiting our spacecraft! </p> </FancyBorder> ); }

在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 children,而是自行约定:将所需内容传入 props,并使用相应的 prop

复制代码
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
function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } // <Contacts /> 和 <Chat /> 之类的 React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递 function Contacts(){ return( <div>Contacts</div> ) } function Chat(){ return( <div>Chat</div> ) } function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); }

特例关系

有些时候,我们会把一些组件看作是其他组件的特殊实例,比如 WelcomeDialog 可以说是 Dialog 的特殊实例。

在 React 中,我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件:

复制代码
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
function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); } function Dialog(props) { return ( <FancyBorder color="blue"> <h1 className="Dialog-title"> {props.title} </h1> <p className="Dialog-message"> {props.message} </p> </FancyBorder> ); } function WelcomeDialog() { return ( <Dialog title="Welcome" message="Thank you for visiting our spacecraft!" /> ); }

最后

以上就是炙热毛巾最近收集整理的关于React 官网核心概念核心概念的全部内容,更多相关React内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(101)

评论列表共有 0 条评论

立即
投稿
返回
顶部