react学习 02

待雾散开,自然能看到想要的风景。

我们都知道,JSXReact.createElement(component, props, ...children)的语法糖。

1
2
3
4
5
6
7
8
9
10
11
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}

---
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);

Diff算法

当组件的root为不同类型的diff

diffing如下

1
2
3
4
5
6
7
<div>
<Counter />
</div>

<span>
<Counter />
</span>

<Counter /> 会被卸载并且它的state会被销毁

同类型的DOM元素比较

1
2
3
<div className="before" title="stuff" />

<div className="after" title="stuff" />

React只改变className

1
2
3
<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

React也只改变color

同类型的React组件

React不会卸载组件(即同一个JS instance),React会通过更新props去匹配新的DOM元素,之后调用componentWillReceiveProps() and componentWillUpdate()

在Children中递归

1
2
3
4
5
6
7
8
9
10
<ul>
<li>first</li>
<li>second</li>
</ul>

<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>

React能匹配firstsecond,并且保证这两棵子树的完整,只是插入third子树。

试想一下,如果thirdfirst之前插入,React不会考虑firstsecond的完整性,而是改变每一个子节点。How a bad performance !

Key

key用来解决上面的问题

1
2
3
4
5
6
7
8
9
10
<ul>
<li key="first">first</li>
<li key="second">second</li>
</ul>

<ul>
<li key="third">third</li>
<li key="first">first</li>
<li key="second">second</li>
</ul>

Render Props

利用render props解决组件复用问题

一个简单的应用场景

需要记录鼠标移动的位置,并用p标签显示出来。

一般会直接封装一个Mouse组件用来记录鼠标的位置并且保存在state状态。并且通过this.statep标签中显示。直接在MouseTracker(父组件中调用就可以了)。

但这并没有实现真正的封装,当我们新增业务需求的时候,比如需要随鼠标渲染出一只猫的image,创建一个Cat组件。把Cat组件放在Mouse组件的render()方法里,通过传入this.state来获取鼠标位置渲染出image。这样就失去复用的的意义了,如果要渲染Dog,Pig,那么每次都要重新创建Mouse组件,MouseWithCatMouseWithPigMouseWithDog

利用render属性,可以实现动态渲染。

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
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}

class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}

handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}

render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}

class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}

注意

(1) render Props的属性名不强制规定为render

(2) children prop也能实现

1
2
3
4
5
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>

(3) 小心用render属性

因为render属性是个函数,向下面这样写就很不好

1
2
3
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>

每次Mouse组件重新渲染的时候都会重新创建render函数。

将render函数作为组件实例方法就行了。(组件一般都会一直在内存里)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MouseTracker extends React.Component {

renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}

render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
-------------要说再见啦感谢大佬的光临~-------------