本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)
Portals
某些情况下,我们希望渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中(默认都是挂载到id
为root
的DOM元素上的)。
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案:
ReactDOM.createPortal(child, container)
- 第一个参数(
child
)是任何可渲染的 React 子元素,例如一个元素,字符串或fragment
; - 第二个参数(
container
)是一个 DOM 元素;
通常来讲,当你从组件的 render
方法返回一个元素时,该元素将被挂载到 DOM 节点中离其最近的父节点:
render() {
// React 挂载了一个新的 div,并且把子元素渲染其中
return (
<div>
{this.props.children}
</div>
);
}
然而,有时候将子元素插入到 DOM 节点中的不同位置也是有好处的:
render() {
// React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
// `domNode` 是一个可以在任何位置的有效 DOM 节点。
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
比如说,我们准备开发一个 Modal 组件,它可以将它的子组件渲染到屏幕的中间位置:
步骤一:修改index.html
添加新的节点
<div id="root"></div>
<!-- 新节点 -->
<div id="modal"></div>
步骤二:编写这个节点的样式:
#modal {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: red;
}
步骤三:编写组件代码
import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
class Modal extends PureComponent {
constructor(props) {
super(props);
}
render() {
return ReactDOM.createPortal(
this.props.children,
document.getElementById("modal")
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Modal>
<h2>我是标题</h2>
</Modal>
</div>
)
}
}
第三方库中的Modal
,如AntDesign
,通常都是通过主动创建一个div
(document.createElement
),然后调用ReactDOM.render()
方法来渲染到自己创建的div
上的。
Fragment
在之前的开发中,我们总是在一个组件中返回内容时包裹一个div
元素:
export default class App extends PureComponent {
render() {
return (
<div>
<h2>当前计数: 0</h2>
<button>+1</button>
<button>-1</button>
</div>
)
}
}
我们会发现多了一个div
元素:
- 这个
div
元素对于某些场景是需要的(比如我们就希望放到一个div
元素中,再针对性设置样式) - 某些场景下这个
div
是没有必要的,比如当前这里我可能希望所有的内容直接渲染到root
中即可;
我们可以删除这个div
吗?
我们又希望可以不渲染这样一个div
应该如何操作呢?
- 使用
Fragment
Fragment
允许你将子列表分组,而无需向 DOM 添加额外节点;
React还提供了Fragment
的段语法:
- 它看起来像空标签
<> </>
;
export default class App extends PureComponent {
render() {
return (
<>
<h2>当前计数: 0</h2>
<button>+1</button>
<button>-1</button>
</>
)
}
}
但是,如果我们需要在Fragment
中添加key
,那么就不能使用段语法:
{
this.state.friends.map((item, index) => {
return (
<Fragment key={item.name}>
<div>{item.name}</div>
<div>{item.age}</div>
</Fragment>
)
})
}
这里是不支持如下写法的:
<key={item.name}>
<div>{item.name}</div>
<div>{item.age}</div>
</>
StrictMode
StrictMode 是一个用来突出显示应用程序中潜在问题的工具。
- 与
Fragment
一样,StrictMode 不会渲染任何可见的 UI; - 它为其后代元素触发额外的检查和警告;
- 严格模式检查仅在开发模式下运行;它们不会影响生产构建;
可以为应用程序的任何部分启用严格模式:
import React from 'react';
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
);
}
- 不会对
Header
和Footer
组件运行严格模式检查;
但是,ComponentOne
和ComponentTwo
以及它们的所有后代元素都将进行检查;
但是检测,到底检测什么呢?
- 识别不安全的生命周期:
class Home extends PureComponent {
UNSAFE_componentWillMount() {
}
render() {
return <h2>Home</h2>
}
}
2. 使用过时的 ref API
class Home extends PureComponent {
UNSAFE_componentWillMount() {
}
render() {
return <h2 ref="home">Home</h2>
}
}
- 使用废弃的
findDOMNode
方法
在之前的React API中,可以通过findDOMNode
来获取DOM,不过已经不推荐使用了,可以自行学习演练一下
- 检查意外的副作用
class Home extends PureComponent {
constructor(props) {
super(props);
console.log("home constructor");
}
UNSAFE_componentWillMount() {
}
render() {
return <h2 ref="home">Home</h2>
}
- 这个组件的
constructor
会被调用两次; - 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次时,是否会产生一些副作用;
- 在生产环境中,是不会被调用两次的;
- 检测过时的context API
早期的Context是通过static
属性声明Context对象属性,通过getChildContext
返回Context对象等方式来使用Context的;
目前这种方式已经不推荐使用,大家可以自行学习了解一下它的用法;