基礎知識梳理 ~ react組件通訊

1 父子通訊

  • 在父組件中的子組件標籤上將父組件的變量,方法經過屬性的形式讓子組件能夠訪問vue

  • 在子組件中經過this.props.屬性名訪問對應變量或方法react

  • 方法應定義爲箭頭函數,避免不一樣組件調用方法致使this指向混亂api

    // 以todos功能爲例
    
    // 父組件
    import React, { Component } from 'react'
    import AddItem from './AddItem'
    import Todolist from './Todolist'
    
    class Content extends Component {
      constructor(props) {
        super(props)
        this.state = {//父組件中的變量
          value: '',
          arrList: []
        }
      }
      // 定義方法
      // 用setState函數修改state中變量,避免直接對state中的變量進行操做
      // 爲了子組件調用父組件方法時不改變this指向,應將方法定義爲箭頭函數
      handleChange = (value) => {
        this.setState({//異步操做,但若在定時器中使用就是同步操做
          value
        })
      }
      handleClick = () => {
        this.setState({
          arrList: [...this.state.arrList, this.state.value],
          value: ''
        })
      }
      deleteClick = (id) => {
        let temparr = this.state.arrList
        temparr.splice(id,1)
        this.setState({
          arrList: temparr
        })
      }
      render() {
        return (
          <div>
            <AddItem
              value = {this.state.value}
              handleChange = {this.handleChange}
              handleClick = {this.handleClick}
            />
            <Todolist
              arrList = {this.state.arrList}
              deleteClick = {this.deleteClick}
            />
          </div>
        )
      }
    }
    
    export default Content
    
    // 子組件 AddItem
    import React, { Component, Fragment } from 'react'
    
    class AddItem extends Component {
      getInpValue = (e) => {
        this.props.handleChange(e.target.value)
      }
      render() {
        return (
          <Fragment>
            <input type = "text" value = {this.props.value} onChange = {this.getInpValue} />
            <button onClick = {this.props.handleClick}>添加任務</button>
          </Fragment>
        )
      }
    }
    
    export default AddItem
    
    // 子組件 Todolist
    import React, { Component } from 'react'
    
    class Todolist extends Component {
      // 使用父組件方法須要傳參,應在子組件方法中調用,經過bind綁定參數給子組件方法傳參
      // 雖然父組件定義方法時箭頭函數時,子組件給該方法bind綁定參數也能夠,但以上方法更可靠
      deleteItem (i) {
        this.props.deleteClick(i)
      }
      render() {
        return (
          <ul>
            {
              this.props.arrList.map((item,index) => {
                return (
                  <li key = {index}>
                    {item}
                    <span onClick = {this.props.deleteClick.bind(this,index)}>X</span>
                  </li>
                )
              })
            }
          </ul>
        )
      }
    }
    
    export default Todolist
    複製代碼

2 利用context跨組件通訊

  • context的功能數組

    • 在react沒有相似vue中的事件總線來解決這個問題
    • 咱們只能藉助它們共同的父級組件來實現,將非父子關係裝換成多維度的父子關係
    • react提供了context api來實現跨組件通訊, React 16.3以後的contextapi較以前的好用
  • 使用方法dom

    • React.createContext() 咱們須要用到createContext建立上下文異步

    • 返回的是一個對象 對象內有2個組件ide

      • Provider:設置共享狀態及方法
        • 經過value去傳遞
      • Consumer:接收共享狀態或方法
        • 只要被 Provider這個組件包裹的全部組件均可以經過Consumer接收
        • Consumer 組件 內部必須返回一個函數,這個函數會接受到 Provider傳遞過來的狀態及方法
    • 示例:按鈕實現加/減函數

      • index.js
      // index.js
      import React from 'react';
      import ReactDOM from 'react-dom';
      import App from './App';
      import CountProvider from './context'
      // 組件標籤內就算有內容也不會自動去解析,需在組件內部經過this.props.children獲取使用
      ReactDOM.render(
        (
          <CountProvider> <App /> </CountProvider>
        ),
        document.getElementById('root'));
      複製代碼
      • context.jspost

        import React, { createContext, Component, Fragment } from 'react'
        // Fragment不會生成DOM標籤,通常用來作根標籤,也能夠用<></>代替
        const {Consumer, Provider} = createContext()
        
        class CountProvider extends Component {
          constructor(props) {
            super(props)
            this.state = {
              num: 10
            }
          }
          addNum = () => {
            this.setState({
              num: this.state.num + 1
            })
          }
          minusNum = () => {
            this.setState({
              num: this.state.num - 1
            })
          }
          // 此處this.props.children獲取到的是APP組件,故在App組件中使用的組件均可以能夠經過Consumer接收
          render() {
            return (
              <Fragment> <Provider value = {{ num: this.state.num, addNum: this.addNum, minusNum: this.minusNum }}> {this.props.children} </Provider> </Fragment>
            )
          }
        }
        // export 導出語法有
        	// export let/const 變量名
        	// 或export function 函數名 () {}
        	// 或export {變量名}
        // export default 可直接導出 變量,但只能使用一次
        export default CountProvider
        export {Consumer, Provider}
        複製代碼
      • App.js動畫

        import React, { Component } from 'react'
        import CountButton from './components/button'
        import Count from './components/count'
        
        class App extends Component {
          render () {
            return (
              <div> <CountButton type='add'>+</CountButton> <Count /> <CountButton type='minus'>-</CountButton> </div>
            )
          }
        }
        
        export default App;
        複製代碼
      • /count/index.js

        import React, { Component } from "react";
        import {Consumer} from '../../context'
        
        class Count extends Component {
          render() {
            return (
              <Consumer> { ({num}) => { return ( <span>{num}</span> ) } } </Consumer>
            )
          }
        }
        
        export default Count
        複製代碼
      • /button/index.js

        import React, { Component } from 'react'
        import { Consumer } from '../../context'
        
        class CountButton extends Component {
          render() {
            return (
              <Consumer> { ({addNum, minusNum})=>{ const setFunc = this.props.type === 'add' ? addNum : minusNum return ( <button onClick={setFunc}> {this.props.children} </button> ) } } </Consumer>
            )
          }
        }
        
        export default CountButton
        複製代碼

3 ref通訊

  • 適用場景

    • 對DOM 元素焦點的控制、內容選擇或者媒體播放
    • 經過對DOM元素控制,觸發動畫特效
    • 集成第三方 DOM 庫
  • 避免使用

    • 避免使用 refs 去作任何能夠經過聲明式實現來完成的事情
    • 例如,避免在Dialog、Loading、Alert等組件內部暴露 open(), show(), hide(),close()等方法,最好經過 isXX屬性的方式來控制
  • 使用限制

    • 函數組件內部不支持使用 字符串 refs (支持 createRef 、 useRef 、 回調 Ref)
    • 不能在函數組件上使用 ref 屬性,由於函數組件沒有實例
    • useRef 僅限於在函數組件內使用

3.1 String類型的refs

  • 在元素或組件標籤上設置ref屬性

    <button ref = 'btnRef' onClick = {this.handleClick}>dianwo</button>
    <Son ref = 'sonRef' />
    複製代碼
  • 經過this.refs獲得全部設置了ref屬性的元素或組件實例

    • 如經過this.refs.sonRef可訪問Son組件中的狀態和方法
    • 經過this.refs.btnRef操做真實button DOM
  • 過期的API,官方建議使用 回調函數 或者 createRef API的方式來替換

  • 函數組件內部不支持使用

3.2 createRef API

  • 在React16.3 版本中引入的

  • 使用 React.createRef() 建立 Refs,並經過 ref 屬性附加至 React 元素上。一般在構造函數中,將 Refs 分配給實例屬性,以便在整個組件中引用。

  • ref 被傳遞給 render 中的元素時,對該節點的引用能夠在 refcurrent 屬性中訪問

  • ref 的值根據節點的類型而有所不一樣

  • ref 屬性用於 HTML 元素時,構造函數中使用 React.createRef() 建立的 ref 接收底層 DOM 元素做爲其 current 屬性

  • ref 屬性用於自定義的 class 組件時, ref 對象接收組件的掛載實例做爲其 current 屬性

  • 不能在函數組件上使用 ref 屬性,由於函數組件沒有實例

this.sonRef = createRef()
<Son ref = {this.sonRef} />
this.sonRef.current
複製代碼

3.3 useRef API

  • useRef 是 React16.8 中引入的,只能在函數組件中使用

  • 使用 React.useRef() 建立 Refs,並經過 ref 屬性附加至 React 元素上

  • 返回的 ref 對象在組件的整個生命週期內保持不變

  • ref 被傳遞給 React 元素時,對該節點的引用能夠在 refcurrent 屬性中訪問

import React from 'react';

export default function MyInput(props) {
    const inputRef = React.useRef(null);
    React.useEffect(() => {
        inputRef.current.focus();
    });
    return (
        <input type="text" ref={inputRef} /> ) } 複製代碼

3.4 回調形式的refs

  • 在react較早的版本中,咱們推薦使用 回調形式的refs

  • 這種方式能夠幫助咱們更精細的控制什麼時候 Refs 被設置和解除

  • 將回調函數傳遞給 React元素ref 屬性。這個函數接受 React 組件實例 或 HTML DOM 元素做爲參數,將其掛載到實例屬性上

  • React 會在組件掛載時,調用 ref 回調函數並傳入 DOM元素(或React實例),當卸載時調用它並傳入 null。在 componentDidMountcomponentDidUpdate 觸發前,React 會保證 Refs 必定是最新的

import React from 'react';

export default class MyInput extends React.Component {
    constructor(props) {
        super(props);
        this.inputRef = null;
        this.setTextInputRef = (ele) => {
            this.inputRef = ele;
        }
    }

    componentDidMount() {
        this.inputRef && this.inputRef.focus();
    }
    render() {
        return (
            <input type="text" ref={this.setTextInputRef}/> ) } } 複製代碼

參考: 你想知道的關於 Refs 的知識都在這了 ---劉小夕

基礎決定將來,一步一個腳印

相關文章
相關標籤/搜索