「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中文文档

相关文章
|
5月前
|
前端开发 JavaScript
深入理解并实践React Hooks —— useEffect与useState
深入理解并实践React Hooks —— useEffect与useState
203 1
|
4月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
358 2
|
5月前
|
JavaScript 前端开发 算法
React技术栈-虚拟DOM和DOM diff算法
这篇文章介绍了React技术栈中的虚拟DOM和DOM diff算法,并通过一个实际案例展示了如何使用React组件和状态管理来实现动态更新UI。
61 2
|
3月前
|
前端开发 JavaScript API
探究 React Hooks:如何利用全新 API 优化组件逻辑复用与状态管理
本文深入探讨React Hooks的使用方法,通过全新API优化组件逻辑复用和状态管理,提升开发效率和代码可维护性。
|
5月前
|
JavaScript 前端开发
react学习(3)创建虚拟dom的两种方式
react学习(3)创建虚拟dom的两种方式
184 67
|
3月前
|
前端开发 JavaScript
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
手敲Webpack 5:React + TypeScript项目脚手架搭建实践
|
4月前
|
JavaScript 前端开发 算法
React 虚拟 DOM 深度解析
【10月更文挑战第5天】本文深入解析了 React 虚拟 DOM 的工作原理,包括其基础概念、优点与缺点,以及 Diff 算法的关键点。同时,分享了常见问题及解决方法,并介绍了作者在代码/项目上的成就和经验,如大型电商平台的前端重构和开源贡献。
82 3
|
5月前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
54 3
|
5月前
|
XML 缓存 JavaScript
提升对前端的认知,不得不了解Web API的DOM和BOM
该文章强调了在前端开发中理解和掌握DOM(文档对象模型)和BOM(浏览器对象模型)的重要性,并介绍了它们的相关操作和应用。
提升对前端的认知,不得不了解Web API的DOM和BOM
|
5月前
|
JavaScript 前端开发
react字符串转为dom标签,类似于Vue中的v-html
本文介绍了在React中将字符串转换为DOM标签的方法,类似于Vue中的`v-html`指令,通过使用`dangerouslySetInnerHTML`属性实现。
153 0
react字符串转为dom标签,类似于Vue中的v-html