2

react 16学习(三)

 2 years ago
source link: https://snayan.github.io/post/learn_react16_part3/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

react 16学习(三)

April 04, 2018/「 react 」/ Edit on Github ✏️

文章目录

ReactDom 在 16 版本中也新增了一些新的功能,比如createPortalhydrate。今天主要学习一下createPortal。 先看下 ReactDom 的大致包含的一些属性和方法。

const ReactDom = {
  createPortal, //reacte16中新增的
  hydrate, //reacte16中新增的
  findDomNode,
  render,
  unmountComponentAtNode,
  flushSync,
  unstable_renderSubtreeIntoContainer,
  unstable_createPortal, //这个实际就是现在的createPortal,将在React17版本中移除
  unstable_batchedUpdates,
  unstable_deferredUpdates,
  unstable_flushControlled,
}

可以看到我们熟悉的findDomNoderenderunmountComponentAtNode。这里面新增了两个新的方法createPortalhydrate。以 unstable_开头的表示的是当前版本中存在的方法,但可能会在后续版本中改动或移除,所以不建议使用。

createPortal

先来看下这个方法的签名:

export function createPortal(
  children: ReactNodeList,
  container: DOMContainer,
  key: ?string = null,
): ReactPortal {
  return {
    // This tag allow us to uniquely identify this as a React Portal
    $$typeof: REACT_PORTAL_TYPE,
    key: key == null ? null : '' + key,
    children,
    containerInfo,
    implementation:null,
  };
}

可以看到它接受三个参数,第一个 children,要渲染的节点元素,第二个是 container,要渲染的容器,第三个是可选的参数 key,这个参数一般没用,默认为 null。 它的作用就是在任意合法的 dom 节点都可以用作渲染的容器,而不仅仅是当前父级节点。下面为官方的说明:

Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component.

通常情况下,我们在render中挂载 React child 到它最近的父节点下,例如

render() {
  // React mounts a new div and renders the children into it
  return (
    <div>
      {this.props.children}
    </div>
  );
}

createPortal却可以使得 React child 挂载到任何合法的 dom 节点下

render() {
  // React does *not* create a new div. It renders the children into `domNode`.
  // `domNode` is any valid DOM node, regardless of its location in the DOM.
  return ReactDOM.createPortal(
    this.props.children,
    domNode,
  );
}

虽然createPortal可以挂载到任何合法的 dom 容器中,但是它在 React tree 中的层次结构是不会受影响的,比如事件冒泡。在用createPortal渲染的组件中触发一个事件,依然可以被它在 React tree 中的父级节点捕获到。看下面的例子: 例如有下面的 html 结构

<html>
  <body>
    <div id="app-root"></div>
    <div id="modal-root"></div>
  </body>
</html>

在 React tree 中,app-root 节点依然可以捕获到 modal-root 节点冒泡出来的事件。

// These two containers are siblings in the DOM
const appRoot = document.getElementById("app-root")
const modalRoot = document.getElementById("modal-root")

class Modal extends React.Component {
  constructor(props) {
    super(props)
    this.el = document.createElement("div")
  }

  componentDidMount() {
    // The portal element is inserted in the DOM tree after
    // the Modal's children are mounted, meaning that children
    // will be mounted on a detached DOM node. If a child
    // component requires to be attached to the DOM tree
    // immediately when mounted, for example to measure a
    // DOM node, or uses 'autoFocus' in a descendant, add
    // state to Modal and only render the children when Modal
    // is inserted in the DOM tree.
    modalRoot.appendChild(this.el)
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el)
  }

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

class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { clicks: 0 }
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    // This will fire when the button in Child is clicked,
    // updating Parent's state, even though button
    // is not direct descendant in the DOM.
    this.setState(prevState => ({
      clicks: prevState.clicks + 1,
    }))
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools to observe that the button is not a child
          of the div with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>
    )
  }
}

function Child() {
  // The click event on this button will bubble up to parent,
  // because there is no 'onClick' attribute defined
  return (
    <div className="modal">
      <button>Click</button>
    </div>
  )
}

ReactDOM.render(<Parent />, appRoot)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK