「React进阶」 React全部api解读+基础实践大全(夯实基础2万字总结) (React DOM)

简介: 一份不错的 React 学习指南

接下来,我们来一起研究react-dom中比较重要的api

react-dom.jpg

render

render 是我们最常用的react-domapi,用于渲染一个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上。此时就很适合createPortalAPI。

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>
    }
}

效果

createPortal.jpg

我们可以看到,我们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>
    }
}

效果

batch1.jpg

渲染次数一次。

批量更新条件被打破

 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)
    })
  }

效果

batch2.jpg

渲染次数三次。

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>
    }
}

先不看答案,点击一下按钮,打印什么呢?

我们来点击一下看看

flushSync.gif

打印 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>
    }
}

效果:

findNodedom.gif

我们完全可以将外层容器用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>
    }
}

效果

unmounted.gif

本文通过react组件层面,工具层面,hooks层面,react-dom了解了api的用法,希望看完的同学,能够对着文章中的demo自己敲一遍,到头来会发现自己成长不少。

最后, 送人玫瑰,手留余香,觉得有收获的朋友可以给笔者点赞,关注一波 ,陆续更新前端超硬核文章。

提前透漏:接下来会出一部揭秘react事件系统的文章。

感兴趣的同学请关注公众号 前端Sharing 持续推送优质好文

往期react文章

文章中,对于其他没有讲到的react-hooks,建议大家看react-hooks三部曲。

react-hooks三部曲

react进阶系列

react源码系列

开源项目系列

参考文档

react中文文档

相关文章
|
1月前
|
Java API
JavaSE——常用API进阶二(6/8)-ZoneId、ZoneDateTime、Instant(常见方法、用法示例)
JavaSE——常用API进阶二(6/8)-ZoneId、ZoneDateTime、Instant(常见方法、用法示例)
19 1
|
4天前
|
XML 前端开发 JavaScript
JavaScript进阶 - AJAX请求与Fetch API
【7月更文挑战第3天】前端开发中的异步基石:AJAX与Fetch。AJAX,使用XMLHttpRequest,处理跨域、回调地狱和错误处理。Fetch,基于Promise,简化请求,但需注意默认无跨域头和HTTP错误处理。两者各有优劣,理解其问题与解决策略,能提升前端应用的性能和用户体验。
|
10天前
|
JavaScript 前端开发 算法
虚拟DOM是React的关键技术,它是个轻量的JS对象树,模拟实际DOM结构。
【6月更文挑战第27天】虚拟DOM是React的关键技术,它是个轻量的JS对象树,模拟实际DOM结构。当状态改变,React不直接修改DOM,而是先构建新的虚拟DOM树。通过 diff 算法比较新旧树,找到最小变更,仅更新必要部分,提高性能,避免频繁DOM操作。虚拟DOM还支持跨平台应用,如React Native。它优化了更新流程,简化开发,并提升了用户体验。
16 1
|
1天前
|
JavaScript
react18【系列实用教程】useRef —— 创建 ref 对象,获取 DOM (2024最新版)
react18【系列实用教程】useRef —— 创建 ref 对象,获取 DOM (2024最新版)
4 0
|
1天前
|
网络架构
react18【系列实用教程】react-router-dom —— 路由管理 (2024最新版)
react18【系列实用教程】react-router-dom —— 路由管理 (2024最新版)
7 0
|
1月前
|
前端开发 API
|
1月前
|
存储 Java API
JavaSE——常用API进阶二(8/8)-Arrays、Comparable、Comparator(Arrays类提供的的常见方法、用法示例)
JavaSE——常用API进阶二(8/8)-Arrays、Comparable、Comparator(Arrays类提供的的常见方法、用法示例)
23 2
|
1月前
|
安全 Java API
JavaSE——常用API进阶二(7/8)-DateTimeFormatter、Period、Duration(常见方法、用法示例)
JavaSE——常用API进阶二(7/8)-DateTimeFormatter、Period、Duration(常见方法、用法示例)
35 2
|
1月前
|
安全 Java API
JavaSE——常用API进阶二(5/8)-JDK 8新增的时间API,LocalDate、LocalTime、LocalDateTime
JavaSE——常用API进阶二(5/8)-JDK 8新增的时间API,LocalDate、LocalTime、LocalDateTime
21 2
|
1月前
|
存储 Java API
JavaSE——常用API进阶二(4/8)-秒杀案例(需求与分析、代码与运行结果)、Calendar(Calendar日历类的常见方法、用法示例)
JavaSE——常用API进阶二(4/8)-秒杀案例(需求与分析、代码与运行结果)、Calendar(Calendar日历类的常见方法、用法示例)
25 1