react学习笔记(二)

JSX语法

JSX中使用变量就是一个{}花括号括起来就行了,对属性而言,使用””双引号赋值字面量或使用{}插入js表达式。

1
2
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

react DOM使用小驼峰的写法。

标签里面如果没有内容,用直接使用</>闭合标签,如果包含许多子元素,用()

1
2
3
4
5
6
const element = <img src={user.avatarUrl} />;
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);

在安全方面,JSX直接使用用户输入的内容,因为react DOM会对输入进行转译,转为字符串避免

Babel将JSX转译React.createElement() 函数,函数创建一个对象,这样的对象被称为react元素。

1
2
3
4
5
6
7
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
}

渲染

元素渲染

react元素是创建开销极小的对象,使用ReactDOM.render()将react元素渲染到dom上

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

更新渲染

react元素不可变,代表某个特定时期的ui,更新ui的方法是创建一个全新的元素,并使用ReactDOM.render()。一般来说,ReactDOM.render()只会调用一次。官网使用setInterval定时器来展示时间的更新。

与vue的虚拟DOM类似,也是只更新改变了部分。

组件

组件包含函数组件和class组件

函数组件是指接收的参数是一个对象,并返回一个react元素的组件。

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

class组件使用es6中的class继承React.Component来定义组件

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

渲染

渲染用户自定义组件发生了什么

1
2
3
4
5
6
7
function Welcome(props) {  return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);

首先,定义好welcome组件和element,调用ReactDOM.render,对传入的element即<Welcome name=”Sara” />中的自定义组件welcome进行调用,传入{name: ‘Sara’},组件返回<h1>Hello, Sara</h1>。然后在id为root的元素中渲染<h1>Hello, Sara</h1>。

注意自定义组件的名称需要大写,小写会被react认为是原生标签。

组合组件

就是在组件中也可以使用其他组件

提取组件

将耦合的代码变成一个个组件,提高可维护性

props不可更改

也就是说react组件对于传入的props,不能修改它的值

state与生命周期

state与props类似,state是私有并完全受控于当前组件。

将函数组件转为class组件主要注意的是将props替换为this.props。

为class组件添加局部state的步骤:this.props替换成this.state。添加constructor构造函数,为this.state赋初值,通过props来调节父类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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>
);
}
}

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

注意JS中为子类添加构造函数需要super开头,并且里面的语句都以;结尾。

生命周期

componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行

componentWillUnmount() 生命周期方法中清除

使用 this.setState() 来时刻更新组件的state

使用state需要注意:

  • 不直接修改state,而是使用setState(),在构造函数中唯一给this.state赋值。
1
this.setState({comment: 'Hello'});
  • state与props可能是异步更新的,通过让state接收一个函数而不是对象来解决
1
2
3
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
  • state的更新会被合并

    会将更新的对象合并到当前的state,可以使用setState单独更新state中的对象

数据向下流动

组件无法获知其他组件的状态,state除了拥有和设置了它的组件,其他组件是无法访问的。但是组件也可以选择将state作为props向下传递给子组件

1
2
3
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
自定义组件:
<FormattedDate date={this.state.date} />

子组件是无法知道参数传入的数据是来自父组件的state还是props,还是手动输入的。单向数据流是指state总是属于特定的组件,并且只能影响树中”低于”它们的组件。

事件处理

在react的JSX语法中需要使用{}传入函数作为事件处理函数,并且不能返回false阻止默认行为,必须使用preventDefault。

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

为元素添加监听器可以在初始渲染时就添加。

当使用class定义时,常见做法是将事件处理函数声明以一个方法,特别需要注意其中this的绑定

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

这样绑定是为了当(子组件)调用时指向的是父组件的this,因为JS中class是不会默认绑定this的,也可以使用箭头函数来绑定。

1
2
3
4
5
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);

但是存在一个性能问题:当函数作为props传入子组件时,组件有可能进行额外渲染。所以建议在构造函数中绑定this或者使用class fields(实验阶段)

1
2
3
handleClick = () => {
console.log('this is:', this);
}

传参

向事件处理函数传参,可以使用以下两种方法,箭头函数和通过Function.prototype.bind

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

使用箭头函数,事件对象需要显式传递,bind方法的话,事件对象及更多参数通过隐式传递

条件渲染

通过if或条件运算符来选择渲染哪个组件,可以用变量来保存元素,然后直接使用变量

1
2
3
4
5
6
7
8
9
10
11
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);

运算符

与&&

1
2
3
4
5
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}

直接在{}中使用&&,当左边为true时右边才会执行,否则不执行。

三目运算同样也可以使用。

阻止组件渲染

通过render方法返回null,让组件不进行渲染。

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 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')
);

这段官网的例子中,子组件WarningBanner是否渲染取决于Page中showWarning的值,组件WarningBanner需要对props传入的warn值进行判断,看是否返回null。

列表&key

渲染多个组件

1
2
3
4
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);

用map方法来遍历生成5个li标签,然后直接渲染listItems。

直接运行会有警告,原因时缺少key属性

1
2
3
4
5
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);

通过toString来作为key,来标识每个元素,通常我们是通过id作为key,不建议用index,因为当顺序发生变化时,使用index会导致性能变差。

key值应该在被使用时才指定,或者在map()中设置。

key在兄弟节点中需要是唯一的,不需要全局唯一。

查看评论