有關元件的補充~~~~~~~

gercke發表於2020-10-30

ref轉發

import React, { PureComponent, createRef, forwardRef } from 'react';

class Home extends PureComponent {
  render() {
    return <h2>Home</h2>
  }
}

// 高階元件forwardRef
const Profile = forwardRef(function(props, ref) {
  return <p ref={ref}>Profile</p>
})

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

    this.titleRef = createRef();
    this.homeRef = createRef();
    this.profileRef = createRef();
  }

  render() {
    return (
      <div>
        <h2 ref={this.titleRef}>Hello World</h2>
        <Home ref={this.homeRef}/>

        <Profile ref={this.profileRef} name={"why"}/>

        <button onClick={e => this.printRef()}>列印ref</button>
      </div>
    )
  }

  printRef() {
    console.log(this.titleRef.current);
    console.log(this.homeRef.current);
    console.log(this.profileRef.current);
  }
}

Portals

某些情況下,我們希望渲染的內容獨立於父元件,甚至是獨立於當前掛載到的DOM元素中(預設都是掛載到id為root的DOM元素上的)。

Portal 提供了一種將子節點渲染到存在於父元件以外的 DOM 節點的優秀的方案:

  • 第一個引數(child)是任何可渲染的 React 子元素,例如一個元素,字串或 fragment;
  • 第二個引數(container)是一個 DOM 元素;
ReactDOM.createPortal(child, container)

一般情況下,當你從元件的 render 方法返回一個元素時,該元素將被掛載到 DOM 節點中離其最近的父節點:

render() {
  // React 掛載了一個新的 div,並且把子元素渲染其中
  return (
    <div>      
      {this.props.children}
    </div>  
  );
}

然而,有時候將子元素插入到 DOM 節點中的不同位置也是有好處的:

render() {
  // React 並*沒有*建立一個新的 div。它只是把子元素渲染到 `domNode` 中。
  // `domNode` 是一個可以在任何位置的有效 DOM 節點。
  return ReactDOM.createPortal(
    this.props.children,
    domNode  
  );
}

比如說,我們準備開發一個Modal元件,它可以將它的子元件渲染到螢幕的中間位置:

步驟一:修改index.html新增新的節點

<div id="root"></div>
<!-- 新節點 -->
<div id="modal"></div>

步驟二:編寫這個節點的樣式:

#modal {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background-color: red;
}

步驟三:編寫元件程式碼

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

class Modal extends PureComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById("modal")
    )
  }
}

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Modal>
          <h2>我是標題</h2>
        </Modal>
      </div>
    )
  }
}

Fragment

在之前的開發中,我們總是在一個元件中返回內容時包裹一個div元素:

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <h2>當前計數: 0</h2>
        <button>+1</button>
        <button>-1</button>
      </div>
    )
  }
}

某些場景下這個div是沒有必要的,比如當前這裡我可能希望所有的內容直接渲染到root中即可;

我們又希望可以不渲染這樣一個div應該如何操作呢?

  • 使用Fragment
  • Fragment 允許你將子列表分組,而無需向 DOM 新增額外節點;
import React, { PureComponent, Fragment } from 'react';

export default class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <h2>當前計數: 0</h2>
        <button>+1</button>
        <button>-1</button>
      </Fragment>
    )
  }
}

React還提供了Fragment的 段語法

  • 它看起來像空標籤 <> </>;
export default class App extends PureComponent {
  render() {
    return (
      <>
        <h2>當前計數: 0</h2>
        <button>+1</button>
        <button>-1</button>
      </>
    )
  }
}

但是,如果我們需要在Fragment中新增key,那麼就不能使用段語法

render() {
    return (
      <>
        <h2>當前計數: {this.state.counter}</h2>
        <button onClick={e => this.increment()}>+1</button>
        <div>
          {
            this.state.friends.map((item, index) => {
              return (
                <Fragment key={item.name}>
                  <div>{item.name}</div>
                  <p>{item.age}</p>
                  <hr/>
                </Fragment>
              )
            })
          }
        </div>
      </>
    )
  }

相關文章