react中的DOM操作

小火柴的藍色理想發表於2018-04-04

前面的話

  某些情況下需要在典型資料流外強制修改子代。要修改的子代可以是 React 元件例項,也可以是 DOM 元素。這時就要用到refs來操作DOM

 

使用場景

  下面是幾個適合使用 refs 的情況

  1、處理焦點、文字選擇或媒體控制

  2、觸發強制動畫

  3、整合第三方 DOM 庫

  如果可以通過宣告式實現,則儘量避免使用 refs

  [注意]不要在 Dialog 元件上直接暴露 open() 和 close() 方法,最好傳遞 isOpen 屬性

 

ref

  React 支援給任意元件新增特殊屬性。ref 屬性接受一個回撥函式,它在元件被載入或解除安裝時會立即執行

  [注意]在元件mount之後再去獲取ref。componentWillMount和第一次render時都獲取不到,在componentDidMount才能獲取到

【HTML元素】

  當給 HTML 元素新增 ref 屬性時,ref 回撥接收了底層的 DOM 元素作為引數

  React 元件在載入時將 DOM 元素傳入 ref 的回撥函式,在解除安裝時則會傳入 nullref 回撥會在componentDidMount 或 componentDidUpdate 這些生命週期回撥之前執行。

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }
  focus() {
    this.textInput.focus();
  }
  render() {
    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focus}
        />
      </div>
    );
  }
}

  更簡短的寫法如下

ref={input => this.textInput = input}

【類元件】

  當 ref 屬性用於使用 class 宣告的自定義元件時,ref 的回撥接收的是已經載入的 React 例項

class AutoFocusTextInput extends React.Component {
  componentDidMount() {
    this.textInput.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

  [注意]這種方法僅對 class 宣告的 CustomTextInput 有效

【函式式元件】

  不能在函式式元件上使用 ref 屬性,因為它們沒有例項

【對父元件暴露DOM節點】

  在子節點上暴露一個特殊的屬性。子節點將會獲得一個函式屬性,並將其作為 ref 屬性附加到 DOM 節點。這允許父代通過中介軟體將 ref 回撥給子代的 DOM 節點

  該方法適用於類元件和函式式元件

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

  在上面的例子中,Parent 將它的 ref 回撥作為一個特殊的 inputRef 傳遞給 CustomTextInput,然後 CustomTextInput 通過 ref 屬性將其傳遞給 <input>。最終,Parent 中的 this.inputElement 將被設定為與 CustomTextInput 中的 <input> 元素相對應的 DOM 節點

 

非受控元件

  要編寫一個非受控元件,而非為每個狀態更新編寫事件處理程式,可以使用 ref 從 DOM 獲取表單值

  [注意]可能通過e.target.value取得DOM值,而不用繫結react

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={(input) => this.input = input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

  由於非受控元件將真實資料儲存在 DOM 中,因此在使用非受控元件時,更容易同時整合React和非React程式碼

【預設值】

  在 React 的生命週期中,表單元素上的 value 屬性將會覆蓋 DOM 中的值。使用非受控元件時,通常希望 React 可以為其指定初始值,但不再控制後續更新。要解決這個問題,可以指定一個 defaultValue 屬性而不是 value

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={(input) => this.input = input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

  同樣,<input type="checkbox"> 和 <input type="radio"> 支援 defaultChecked<select> 和 <textarea> 支援 defaultValue

 

ReactDOM

  react-dom這個軟體包提供了針對DOM的方法,可以在應用的頂級域中呼叫,也可以在有需要的情況下用作跳出React模型的出口。但大部分元件都不應該需要使用這個包

render()
unmountComponentAtNode()
findDOMNode()

【render()】

ReactDOM.render(
  element,
  container,
  [callback]
)

  渲染一個React元素,新增到位於提供的container裡的DOM元素中,並返回這個元件的一個 引用 (或者對於無狀態元件返回null)

  如果這個React元素之前已經被渲染到container裡去了,這段程式碼就會進行一次更新,並且只會改變那些反映元素最新狀態所必須的DOM元素

【unmountComponentAtNode()】

ReactDOM.unmountComponentAtNode(container)

  從DOM元素中移除已掛載的React元件,清除它的事件處理器和state。如果容器內沒有掛載任何元件,這個函式什麼都不會幹。 有元件被解除安裝的時候返回true,沒有元件可供解除安裝時返回 false

【findDOMNode()】

ReactDOM.findDOMNode(component)

  如果這個元件已經被掛載到DOM中,函式會返回對應的瀏覽器中生成的DOM元素 。需要從DOM中讀取值時,比如表單的值,或者計算DOM元素的尺寸,這個函式會非常有用。 大多數情況下,可以新增一個指向DOM節點的引用,從而完全避免使用findDOMNode 這個函式。當 render 返回 null 或者 false 時, findDOMNode 也返回 null

 

新ref

  版本16.3 之前,React 有兩種提供 ref 的方式:字串和回撥,因為字串的方式有些問題,所以官方建議使用回撥來使用 ref。而現在引入的 createRef API,據官方說是一種零缺點的使用 ref 的方式,回撥方式也可以讓讓路了

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

   然後使用current屬性,即可獲得當前元素

this.myRef.current

  典型應用如下所示

  constructor(props){
    super(props)
    this.Mask = React.createRef()
    this.MenuList = React.createRef()
  }
  handleClick = () => {
    ReactDOM.findDOMNode(this.MenuList.current).classList.toggle('transform-zero')
    ReactDOM.findDOMNode(this.Mask.current).classList.toggle('mask-show')
  }

   [注意]使用styledComponents樣式化的元素暴露的介面是innerRef,而不是ref

<Wrap innerRef={this.itemRef}>

 

  

相關文章