React学习:片段(fragments) 与 插槽(Portals)

一、片段(fragments)

片段(fragments) 可以让你将子元素列表添加到一个分组中,并且不会在DOM中增加额外节点


1、片段长什么样?

render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    React.Fragment>
  );
}

2、为什么会用片段?

看下面例子:

class Columns extends React.Component {
  render() {
    return (
      <div>
        Hello
        World
      div>
    );
  }
}
class Table extends React.Component {
  render() {
    return (
      
        
); } }

为了渲染有效的 HTML , 需要返回多个 元素。如果 的 render() 函数里面使用一个父级 div ,那么最终生成的 HTML 将是无效的,像下面这样:

组件中的输出结果:

<table>
  <tr>
    <div>
      <td>Hellotd>
      <td>Worldtd>
    div>
  tr>
table>

所以,我们介绍 Fragment。


3、使用片段
我们使用 片段 改写上面例子。

class Columns extends React.Component {
  render() {
    return (
      
        
); } } class Table extends React.Component { render() { return (
Hello World
); } }

在正确的

组件中,这个结果输出如下:

<table>
  <tr>
    <td>Hellotd>
    <td>Worldtd>
  tr>
table>

4、简写语法
有一个新的,更短的语法可以用来声明 片段(fragments) 。 它看起来像空标签:

class Columns extends React.Component {
  render() {
    return (
      <>
        
); } }

您可以像使用其他元素一样使用<>,只是它不支持 键(keys) 或 属性(attributes)。

请注意, 目前许多工具都不支持这个简写语法 , 所以你可能需要明确地使用 ,直到工具支持这个语法。


5、带 key 的 片段(fragments)
如果你需要一个带 key 的片段,你可以直接使用 。 一个使用场景是映射一个集合为一个片段数组 — 例如:创建一个描述列表:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // 没有`key`,将会触发一个key警告
        <React.Fragment key={item.id}>
          <dt>{item.term}dt>
          <dd>{item.description}dd>
        React.Fragment>
      ))}
    dl>
  );
}

key 是唯一可以传递给 Fragment 的属性。在将来,我们可能增加额外的属性支持,比如事件处理。


二、插槽(Portals)

插槽(Portals)能将子节点渲染到父组件的 DOM 层次之外。

1、怎么用?

ReactDOM.createPortal(child, container)

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

例:

render() {
  // React *不* 会创建一个新的 div。 它把 children 渲染到 `domNode` 中。
  // `domNode` 可以是任何有效的 DOM 节点,不管它在 DOM 中的位置。
  return ReactDOM.createPortal(
    this.props.children,
    domNode,
  );
}

对于 portal 的一个典型用例是当父组件有 overflow: hidden 或 z-index 样式,但你需要子组件能够在视觉上 “跳出(break out)” 其容器。例如,对话框、hovercards以及提示框。


2、应用

例:效果类似于 点我点我

HTML:

<body>
    <div id="app-root">div>
    <div id="mask-root">div>
body>

CSS:

 #modal-root {position: relative;z-index: 999;}
 .modal {
   color:#fff;background-color: rgba(0,0,0,1);
   position: fixed;top: 0;left: 0;
   height: 100%;width: 100%;display: flex;
   align-items: center;justify-content: center;
 }

React

const appRoot = document.getElementById('app-root');
const maskRoot = document.getElementById('mask-root');
class Mask extends React.Component{
    constructor(props){
        super(props);
        this.el = document.createElement('div');
    }
    componentDidMount(){
        maskRoot.appendChild(this.el);
    }
    componentWillUnmount(){
        maskRoot.removeChild(this.el);
    }
    render(){
        return ReactDOM.createPortal(
            this.props.children,
            this.el
        );
    }
}
class App extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            showMask:false
        }
        this.handler = this.handler.bind(this);
    }
    handler(){
        let showMask = this.state.showMask;
        this.setState({showMask:!showMask});
    }
    render(){
        let showMask = this.state.showMask;
        let mask =  showMask ? 
            
               <div className='modal'>
                    adadsadadad
                   
               div>
            
        : null;
        return (
            <div>
                 This div has overflow: hidden.
                    
                {mask}
            div>
        );
    }
}
ReactDOM.render(,appRoot);

执行结果:
这里写图片描述
点击”show”按钮后,弹出覆盖物
这里写图片描述


3、通过 Portals 进行事件冒泡
尽管 portal 可以被放置在 DOM 树的任何地方,但在其他方面其行为和普通的 React 子节点行为一致。如上下文特性依然能够如之前一样正确地工作,无论其子节点是否是 portal,由于 portal 仍存在于 React tree 中,而不用考虑其在 DOM tree 中的位置。

这包含事件冒泡。一个从 portal 内部会触发的事件会一直冒泡至包含 React tree 的祖先。

例:效果类似于 点我点我

HTML:

<div id="app-root">div>
<div id="mask-root">div>

CSS:

#modal-root {position: relative;z-index: 999;}
.modal {
  color:#fff;background-color: rgba(0,0,0,0.5);
  position: fixed;top: 0;left: 0;
  height: 100%;width: 100%;display: flex;
  align-items: center;justify-content: center;
}

React:

const appRoot = document.getElementById('app-root');
const maskRoot = document.getElementById('mask-root');

class Mask extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

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

  componentWillUnmount() {
    maskRoot.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};
  }

  handleClick = () => {
    this.setState({
      clicks: this.state.clicks + 1
    });
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        

Number of clicks: {this.state.clicks}

div> ); } } function Child() { return ( <div className="modal">Click mediv> ); } ReactDOM.render(, appRoot);

执行结果:
这里写图片描述
每点击一下,number加1:
这里写图片描述

你可能感兴趣的:(web前端,React,React学习)

Hello World