React高級指引

React.lazy

當咱們應用比較龐大的時候, 又或者說當你當前組件的文件比較大,咱們能夠將組件進行賴加載, 在某些狀況下, 它纔會被加載使用, 這個時候 咱們可使用 React.lazy 配合 Suspense 使用react

import React, { Suspense } from 'react';

const LazyPage = React.lazy(() => import('../lazy/Lazy'));


function MyLazyPage() {
  return (
    <div> <Suspense fallback={<div>Loading...</div>}> <LazyPage /> </Suspense> </div>
  );
}

export default MyLazyPage
複製代碼

以上代碼, lazyPage組件 將會 懶加載, 在加載過程當中, 將會顯示 Suspense 定義的fallback 組件數組

配合Route使用react-router

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);
複製代碼

React.lazy 目前只支持==默認導出==(default exports), 若是你想經過==命名導出的方式==dom

能夠參考官方這個例子 ide

image.png

默認導出, 就是你導出的時候, 導出的數據你能夠本身想怎麼命名均可以, 可是命名導出, 你導出的名稱必須一致函數

Context

當你某個屬性會在多個組件中使用到的時候, 咱們能夠考慮使用Context, 它能夠避免你一層層地往下傳遞值, 它有點相似於Vue當中 Provider和 injectui

要想實現這個功能,咱們首先得建立一個context對象 你能夠經過 React.createContext(初始的值) 來 建立一個 context 對象, 它還會返回 ProviderConsumer 對象, 它們都是組件, 你可使用它,this

const nameContext = React.createContext('evanyou') // 建立一個context, 默認值爲evanyou
複製代碼

Provider

Provider 接受一個value 值, value 值能夠更新初始值的, 當value 值發生變化時, 它底下的全部組件都會從新渲染, Provider 及其內部 consumer 組件都不受制於 shouldComponentUpdate 函數spa

<nameContext.Provider value="yaojin">
   <Second /> <OtherPage /> </nameContext.Provider> 複製代碼

將默認的evanyou 值更新成yaojin, 須要注意的是, 儘可能不要將值寫在value中, 若是value 是一個對象 那麼都會不斷返回一個新的對象, 其底下的組件都會從新渲染, 此時能夠考慮將該值放到父組件的state 中3d

如何讀取呢 ? 經過 contextType 屬性 能夠獲取到離最近的那個匹配的 Provider 中讀取context的值 Provider 從新渲染,

class Second extends React.Component {
  static contextType = nameContext

  render() {
    return <div> {this.context} <Transmit value={this.context}/> </div> } } 複製代碼

==contextType 屬性== 僅用在 class 組件中

Consumer

那在函數組件應該如何讀取我須要的值呢 ? 咱們還可使用 Consumer, 在其內部, 你須要使用一個函數來接受context的值, 這個函數應該作爲子元素

function OtherPage() {
  return <nameContext.Consumer> {value => <div >{value} </div> } </nameContext.Consumer> } 複製代碼

Ref

有時候,咱們想操做真實的DOM元素,例如得到某個input的焦點, 或者說或許某個組件的實例, ref 能夠幫咱們實現這一點

經過 React.createRef() 建立一個ref

在元素或者組件中經過 ref屬性 來綁定對應的ref

==ref 只能用於 在 class 組件中使用, 不能再函數組件中使用==

class GetDom extends React.Component {
  constructor(props) {
    super(props)
    // 建立一個ref
    this.inputRef = React.createRef()
    this.focusTextInput = this.foucusInput.bind(this)
  }

  foucusInput() {
    console.log('父組件經過ref拿到該組件實例, 直接調用這個方法了')
    // 經過current能夠訪問到ref綁定的dom元素或者class組件
    this.inputRef.current.focus()
  }

  render() {
    return (
      <div>
        {/* ref獲取inputDom元素 */}
        <input ref={this.inputRef} />
      </div>
    )
  }
}


export default class GetClass extends React.Component {
  constructor (props) {
    super(props)
    this.classRef = React.createRef()
  }

  componentDidMount() {
    this.classRef.current.foucusInput()
  }

  render () {
    return (
      <div> 
        {/* ref獲取GetDom組件實例 */}
        <GetDom ref={this.classRef} />
      </div>
    )
  }
}
複製代碼

不一樣於經過React.createRef()你還能夠經過 ==回調 Refs== 來存儲 ref, 在須要使用的時候, 調用它

export default class GetClass extends React.Component {
  constructor (props) {
    super(props)
    this.refInfo = null
    this.cacheRef = refData => {
      this.refInfo = refData;
    }
    this.triggerChildrenFun = () => {
      if (this.cacheRef) {
        this.refInfo.foucusInput ()
      }
    }
  }

  componentDidMount() {
    this.triggerChildrenFun()
  }

  render () {
    return (
      <div> {/* ref獲取GetDom組件實例 */} <GetDom ref={this.cacheRef} /> </div> ) } } 複製代碼

Fragments

render 須要 建立多個元素的時候,都須要使用 div 來包裹, 若是不想使用這個div來包裹元素, 可使用 Fragments, 它相似於一個<> 的標籤包裹你的元素, 它不支持key 或者 傳遞屬性

若是你想使用key 屬性 , 你能夠顯示得使用 <React.Fragment> 包裹元素

Hoc

Hoc在React中稱爲高階組件, 高階組件的做用在於一些公用邏輯放到高階組件中, 它本質上其實就是一個函數組件,可是它接受另外的組件做爲參數, 並返回該組件

經過高階組件, 咱們能夠把公用的方法以及state都提取都高階組件中, 在經過props的方式傳遞到對應的組件中,

function HocWrap(title) {
  return WrapComponent => {
    return class extends React.Component {
      // 修改在 React-devtool 中高階組件名稱
      static displayName = `HocWrap(${WrapComponent})`

      constructor(prosp) {
        super(prosp)
        this.state = {
          username: '',
          password: '',
          rePassword: '',
        }
        this.onChange = this.onChange.bind(this)
        this.composeChange = this.composeChange.bind(this)
      }

      onChange(stateName, stateValue) {
        this.setState({
          [stateName]: stateValue,
        })
      }

      handleSubmit = e => {
        e.preventDefault()
        const { username, password, rePassword } = this.state
        if (rePassword) {
          alert(
            `用戶名: ${username}, 密碼: ${password}, 確認密碼: ${rePassword}`,
          )
        } else {
          alert(`用戶名: ${username}, 密碼: ${password}`)
        }
      }

      composeChange(name) {
        return e => this.onChange(name, e.target.value)
      }

      render() {
        // 抽離出公用的方法
        const mapFunToProps = {
          composeChange: this.composeChange,
          handleSubmit: this.handleSubmit,
        }

        return (
          <div> <h1>{title}</h1> <WrapComponent {...this.state} {...mapFunToProps} /> </div> ) } } } } 複製代碼

在高階組件中, 若是你想拿到 被包裝 組件的實例, 你可使用ref轉發 來實現, React 提供了一個 React.forwardRef 方法來建立一個組件, 這個方法, 不但能夠接受props, 還能夠接受ref做爲參數, 而後咱們就能夠 經過 React.createRef 來建立 ref , 將這個ref 傳遞到 React.forwardRef 方法中

// Hoc組件內部是一個函數組件 最終返回一個React.forwardRef建立的組件, 它裏面返回的是高階組件中被包裹的組件, 並將ref做爲props進行傳遞
return React.forwardRef((props, ref) => {
      return <HocRef forwardedRef={ref} /> }) 複製代碼

由於高階組件是函數組件, 咱們須要經過一個層class 包裹, 由於ref 只有 class 組件可使用, 只有它能夠建立一個ref, 建立完以後咱們才能夠經過 React.forwardRef 讓函數組件接受到這個ref, 有了這個ref ,咱們就能夠經過props的形式傳遞到內部的包裹組件了, 能夠看上面的代碼, 咱們將ref, 經過props 傳遞給包裹組件了, 那麼內部包裹組件就能夠在經過props讀取到ref, 經過ref屬性 進行綁定關聯了

==只須要明確 ref 只有在class組件中建立而且使用, ref不能像props同樣傳遞給子組件, 因此當遇到函數組件的時候, 咱們想拿到這個ref,就能夠經過React.forwardRef來接收ref, 又或者說你想將這個ref往下傳遞==

// 向外暴露的是高階組件的返回值~包裝了 Register 組件返回了一個新組件
const Hoc = HocWrap('註冊')(Register)

export default class Wrap extends React.Component {
  render() {
    const ref = React.createRef()
    return (
      <div> <Hoc ref={ref}></Hoc> </div>
    )
  }
}
複製代碼

Portals

ReactDOM.createPortal 可讓元素插入到別的dom節點上, 不是單單地插入到父節點上

ReactDOM.createPortal(child, container)
複製代碼

第一個參數(child)是任何可渲染的 React 子元素,例如一個元素,字符串或 fragment。第二個參數(container)是一個 DOM 元素

相關文章
相關標籤/搜索