深入react技术--React与DOM, refs

742 阅读3分钟

导言

从 React 0.14版本后, React将 React中涉及DOM的操作剥离开,目的是为了抽象React,同时适用于web端和移动端。

ReactDOM

react-dom 包提供了DOM特定的方法,可以在你的应用程序的顶层使用,如果你需要的话,也可以作为React模型之外的特殊的操作DOM的接口。

findDOMNode

上一节我们已经讲过组件的生命周期, DOM真正被添加到 HTML中的生命周期方法是 componentDidMount 和 componentDidUpdate 方法。在这两个方法中,我们可以获取真正的 DOM元素。 React提供的获取DOM元素的方法有两种, 其中一种就是ReactDOM提供的 findDOMNode

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class App extends Component {
	componentDidMount() {
    	// this 为当前组件的实例
        const dom = ReactDOM.findDOMNode(this);
    }
    
    render() {}
}

如果在 render中返回 null,那么 findDOMNode 也返回null。 findDOMNode 只对已经挂载的组件有效。

这个方法在严格模式React.StrictMode下会报错

render

ReactDOM.render(element, container[, callback])
渲染一个 React 元素到由container 提供的DOM中, 并且返回组件的一个引用
比如, react脚手架中经典的这段代码

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

unmountComponentAtNode()

ReactDOM.unmountComponentAtNode(container)
从 DOM 移除已装载的 React组件, 并清除器事件处理程序和 state 。
如果在容器中没有挂载组件, 调用此函数什么也不做。 如果组件被卸载,则返回true. 如果没有要卸载的组件,则返回 false
比如,可以这样

ReactDOM.unmountComponentAtNode(document.getElementById('root'))

react插槽

插槽即 : ReactDOM.createPartal(child, container), 由 ReactDOM提供的接口。 可以实现将子节点渲染到父组件DOM层次结构之外的DOM节点。

  • child 可以是任何可渲染的 React子元素。例如,一个元素,字符串,片段(fragment)
  • container 则是一个DOM元素

应用场景 对于 portal 的一个经典用例是当父组件有 overflow:hidden 或 z-index样式,但你需要子组件能够在视觉上 break out 其容器。 例如, 对话框, 提示框

所以一般react组件里的模态框,就是这样实现的
class Modal extends React.Component {
    constructor(props) {
        super(props);

        this.el = document.createElement('div');
    }

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

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

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

export default class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = { showModal: false };

        this.handleShow = this.handleShow.bind(this);
        this.handleHide = this.handleHide.bind(this);
    }

    handleShow() {
        this.setState({ showModal: true });
    }

    handleHide() {
        this.setState({ showModal: false });
    }

    render() {
        const modal = this.state.showModal ? (
            <Modal>
                <div>hello slot</div>
            </Modal>
        ) : null;

        return (
            <div className="app">
                <button onClick={this.handleShow}>show modal</button>
                {modal}
            </div>
        )
    }
}

refs

refs是React组件中非常特殊的props,可以附加到任何一个组件上。 从字面意思上看, 组件被调用时会新建一个该组件的实例, 而refs就会指向这个实例。

它可以是一个回调函数,这个回调函数会在组件被挂载后立即执行。

ref的3种绑定方式

string类型绑定

类似于 vue中的ref绑定方式, 可以通过this.refs 绑定的 ref 的名字获取节点dom
不过这种方法已经被遗弃了

React.createRef()

通过在class中使用React.createRef()方法创建一些变量,可以将这些变量绑定到标签的ref中
那么该变量的current则指向绑定的标签dom

export default class App extends Component {
    constructor(props) {
        super(props);

        this.handleClick = this.handleClick.bind(this);

        this.myRef = React.createRef();
    }

    handleClick() {
        this.myRef.current.focus();
    }

    render() {
        return (
            <div>
                <input type="text" ref={this.myRef}/>
                <input type="button" value="Foucs the text input" onClick={this.handleClick}/>
            </div>
        )
    }
}
函数形式

在class中声明函数,在函数中绑定ref
使用这种方法可以将子组件暴露给父组件以使得父组件能够调用子组件的方法

class Child extends React.Component {
    constructor(props) {
        super (props);

        this.state = {
            count: 1,
        }
    }

    Myclick = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return (
            <div>{this.state.count}</div>
        )
    }
}

export default class App extends React.Component {
    handleClick = () => {
        this.childRef.Myclick()
    }

    render() {
        return (
            <div>
                <Child ref={ref => this.childRef = ref}/>
                <button onClick={this.handleClick}>add</button>
            </div>
        )
    }
}