React学习 01

记录几个有用的特性

Forwarding Refs

React.forwardRef accepts a render function that receives props and ref parameters and returns a React node.

自己写HTML5audio需要在父组件中使用子组件Audio里面的audio DOM元素

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

export default Audio = React.forwardRef((props, ref) => {
return (
<audio
src="https://music.163.com/song/media/outer/url?id=1387152054.mp3"
ref={ref}
{...props}
>
Your browser does not support the <code>audio</code> element.
</audio>
);
});

父组件.js
this.lectureAudio = React.createRef();

<Audio
ref={this.lectureAudio}
onCanPlay={this.handleCanPlay}
onTimeUpdate={this.handleTimeUpdate}
/>

传播属性

两个组件是等效的

1
2
3
4
5
6
7
8
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
const props = {firstName: 'Ben', lastName: 'Hector'};
return <Greeting {...props} />;
}

可以获取特定属性,而不是直接传,这里把otherprops.children传给了button

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Button = props => {
const { kind, ...other } = props;
const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
return <button className={className} {...other} />;
};

const App = () => {
return (
<div>
<Button kind="primary" onClick={() => console.log("clicked!")}>
Hello World!
</Button>
</div>
);
};

一个很有用的栗子

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
父组件.js
<Audio
ref={this.lectureAudio}
handleCanPlay={this.handleCanPlay}
handleTimeUpdate={this.handleTimeUpdate}
/>

一般情况传属性的方式
Audio.js
const Audio = (props) => {
return (
<audio
src={""}
ref={ref}
onCanPlay={props.handleCanPlay}
onTimeUpdate={props.handleTimeUpdate}
>

</audio>
);
}


父组件.js
<Audio
ref={this.lectureAudio}
onCanPlay={this.handleCanPlay}
onTimeUpdate={this.handleTimeUpdate}
/>
直接用props(和以上方式等效)
const Audio = (props) => {
return (
<audio
src={...}
ref={ref}
{...props}
>
</audio>
);
}

代码就变得精简了。

小结

虽然好用,但是官网建议尽量少用,会传一些不必要的属性。

props.children

传递组件的children可以是anything,比如函数

false, null, undefined, 以及 true是不合法children

一个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Repeat(props) {
let items = [];
for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));
}
return <div>{items}</div>;
}

function ListOfTenThings() {
return (
<Repeat numTimes={10}>
{(index) => <div key={index}>This is item {index} in the list</div>}
</Repeat>
);
}

Protals 插槽

博文

使用 Portal 优雅实现“浮”在页面上的组件

当父组件有overflow: hidden; 或者 z-index的样式时候,又希望子结点冲破父容器展示(例如tooltips)

Usage

1
2
3
4
5
6
render() {
return ReactDOM.createPortal(
this.props.children,// ReactChild
domNode//一个DOM元素
);
}

这里没有创建div来包裹this.props.children,而是将它直接渲染到domNode(任意一个DOM结点,无论DOM结点的位置)

react05

React 组件树设计上,Selector 是 Button 的子组件。但是在 DOM 树的角度 SelectorBody 的子节点。

1
2
3
4
5
6
<Button>
<Selector />
</Button>

Button.js
return ReactDOM.createPortal(this.props.children, Body);

注意事件冒泡

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
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>


const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}

componentDidMount() {
modalRoot.appendChild(this.el);
}

componentWillUnmount() {
modalRoot.removeChild(this.el);
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}

class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {clicks: 0};
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}

render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
<p></p>
<Modal>
<Child />
</Modal>
</div>
);
}
}

function Child() {
return (
<div className="modal">
<button>Click</button>
</div>
);
}

ReactDOM.render(<Parent />, appRoot);

此时的DOM树

react05

React组件树

Parent —> Modal —> Child

可见DOM树中Modal组件是它的Parent父组件的兄弟结点。但是由于 React 的事件处理规则,让 portal 的 React 父组件有能力捕获 portal 的冒泡事件。

题外

JS escaped & unescaped output

XSS attack很有用。

一个栗子

有一个页面允许用户输入地址,并且在另外一夜确定,如果用户输入

1
2
3
<script>
alert("Welcome");
</script>

下个页面将会简单地运行这个脚本,特别是当攻击者把脚本放在一个无限循环里,程序就崩了。

因此,用户输入的文本将会被解码(escaped),对应的就是编码(uneasped)。

1
&lt;script&gt;<br/>        alert(&quot;Welcome&quot;);<br/>&lt;/script&gt;

这样,浏览器就会把他当做一个HTML元素而不是script元素了。

-------------要说再见啦感谢大佬的光临~-------------