接下来,我们来一起研究react-dom
中比较重要的api
。
render
render
是我们最常用的react-dom
的 api
,用于渲染一个react
元素,一般react
项目我们都用它,渲染根部容器app
。
ReactDOM.render(element, container[, callback])
使用
ReactDOM.render(
< App / >,
document.getElementById('app')
)
ReactDOM.render
会控制container
容器节点里的内容,但是不会修改容器节点本身。
hydrate
服务端渲染用hydrate
。用法与 render()
相同,但它用于在 ReactDOMServer
渲染的容器中对 HTML
的内容进行 hydrate
操作。
ReactDOM.hydrate(element, container[, callback])
createPortal
Portal
提供了一种将子节点渲染到存在于父组件以外的 DOM
节点的优秀的方案。createPortal
可以把当前组件或 element
元素的子节点,渲染到组件之外的其他地方。
那么具体应用到什么场景呢?
比如一些全局的弹窗组件model
,<Model/>
组件一般都写在我们的组件内部,倒是真正挂载的dom
,都是在外层容器,比如body
上。此时就很适合createPortal
API。
createPortal
接受两个参数:
ReactDOM.createPortal(child, container)
第一个: child
是任何可渲染的 React
子元素
第二个: container
是一个 DOM
元素。
接下来我们实践一下:
function WrapComponent({
children }){
const domRef = useRef(null)
const [ PortalComponent, setPortalComponent ] = useState(null)
React.useEffect(()=>{
setPortalComponent( ReactDOM.createPortal(children,domRef.current) )
},[])
return <div>
<div className="container" ref={
domRef } ></div>
{
PortalComponent }
</div>
}
class Index extends React.Component{
render(){
return <div style={
{
marginTop:'50px' }} >
<WrapComponent>
<div >hello,world</div>
</WrapComponent>
</div>
}
}
效果
我们可以看到,我们children
实际在container
之外挂载的,但是已经被createPortal
渲染到container
中。
unstable_batchedUpdates
在react-legacy
模式下,对于事件,react
事件有批量更新来处理功能,但是这一些非常规的事件中,批量更新功能会被打破。所以我们可以用react-dom
中提供的unstable_batchedUpdates
来进行批量更新。
一次点击实现的批量更新
class Index extends React.Component{
constructor(props){
super(props)
this.state={
numer:1,
}
}
handerClick=()=>{
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
}
render(){
return <div style={
{
marginTop:'50px' }} >
<button onClick={
this.handerClick } >click me</button>
</div>
}
}
效果
渲染次数一次。
批量更新条件被打破
handerClick=()=>{
Promise.resolve().then(()=>{
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
})
}
效果
渲染次数三次。
unstable_batchedUpdate助力
handerClick=()=>{
Promise.resolve().then(()=>{
ReactDOM.unstable_batchedUpdates(()=>{
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
this.setState({
numer : this.state.numer + 1 })
console.log(this.state.numer)
})
})
}
渲染次数一次,完美解决批量更新问题。
flushSync
flushSync
可以将回调函数中的更新任务,放在一个较高的优先级中。我们知道react
设定了很多不同优先级的更新任务。如果一次更新任务在flushSync
回调函数内部,那么将获得一个较高优先级的更新。比如
ReactDOM.flushSync(()=>{
/* 此次更新将设置一个较高优先级的更新 */
this.setState({
name: 'alien' })
})
为了让大家理解flushSync
,我这里做一个demo
奉上,
/* flushSync */
import ReactDOM from 'react-dom'
class Index extends React.Component{
state={
number:0 }
handerClick=()=>{
setTimeout(()=>{
this.setState({
number: 1 })
})
this.setState({
number: 2 })
ReactDOM.flushSync(()=>{
this.setState({
number: 3 })
})
this.setState({
number: 4 })
}
render(){
const {
number } = this.state
console.log(number) // 打印什么??
return <div>
<div>{
number }</div>
<button onClick={
this.handerClick} >测试flushSync</button>
</div>
}
}
先不看答案,点击一下按钮,打印什么呢?
我们来点击一下看看
打印 0 3 4 1 ,相信不难理解为什么这么打印了。
- 首先
flushSync
this.setState({ number: 3 })
设定了一个高优先级的更新,所以3 先被打印 - 2 4 被批量更新为 4
相信这个demo
让我们更深入了解了flushSync
。
findDOMNode
findDOMNode
用于访问组件DOM
元素节点,react
推荐使用ref
模式,不期望使用findDOMNode
。
ReactDOM.findDOMNode(component)
注意的是:
1
findDOMNode
只能用在已经挂载的组件上。2 如果组件渲染内容为
null
或者是false
,那么findDOMNode
返回值也是null
。3
findDOMNode
不能用于函数组件。
接下来让我们看一下,findDOMNode
具体怎么使用的:
class Index extends React.Component{
handerFindDom=()=>{
console.log(ReactDOM.findDOMNode(this))
}
render(){
return <div style={
{
marginTop:'100px' }} >
<div>hello,world</div>
<button onClick={
this.handerFindDom } >获取容器dom</button>
</div>
}
}
效果:
我们完全可以将外层容器用ref
来标记,获取捕获原生的dom
节点。
unmountComponentAtNode
从 DOM
中卸载组件,会将其事件处理器和 state
一并清除。 如果指定容器上没有对应已挂载的组件,这个函数什么也不会做。如果组件被移除将会返回 true
,如果没有组件可被移除将会返回 false
。
我们来简单举例看看unmountComponentAtNode
如何使用?
function Text(){
return <div>hello,world</div>
}
class Index extends React.Component{
node = null
constructor(props){
super(props)
this.state={
numer:1,
}
}
componentDidMount(){
/* 组件初始化的时候,创建一个 container 容器 */
ReactDOM.render(<Text/> , this.node )
}
handerClick=()=>{
/* 点击卸载容器 */
const state = ReactDOM.unmountComponentAtNode(this.node)
console.log(state)
}
render(){
return <div style={
{
marginTop:'50px' }} >
<div ref={
( node ) => this.node = node } ></div>
<button onClick={
this.handerClick } >click me</button>
</div>
}
}
效果
本文通过react
组件层面,工具层面,hooks
层面,react-dom
了解了api
的用法,希望看完的同学,能够对着文章中的demo
自己敲一遍,到头来会发现自己成长不少。
最后, 送人玫瑰,手留余香,觉得有收获的朋友可以给笔者点赞,关注一波 ,陆续更新前端超硬核文章。
提前透漏:接下来会出一部揭秘react
事件系统的文章。
感兴趣的同学请关注公众号 前端Sharing
持续推送优质好文
往期react文章
文章中,对于其他没有讲到的react-hooks
,建议大家看react-hooks
三部曲。
react-hooks三部曲
第一部: react-hooks如何使用
150+
赞👍
react进阶系列
「react进阶」年终送给react开发者的八条优化建议
918+
赞👍「react进阶」一文吃透React高阶组件(HOC)
330+
赞👍
react源码系列
开源项目系列