這是第 78 篇不摻水的原創,想獲取更多原創好文,請搜索公衆號關注咱們吧~ 本文首發於政採雲前端博客: 「混合雙打」之如何在 Class Components 中使用 React Hooks
React 在 v16.8.0 版本中推出了 Hook,做爲純函數組件的加強,給函數組件帶來了狀態、上下文等等;以前一篇關於 React Hooks 的文章介紹瞭如何使用一些官方鉤子和如何自建鉤子,若是想要了解這些內容的同窗能夠點擊這裏。javascript
本文不會再介紹上文中已提到的部分鉤子的基礎使用,而是主要着眼解決一些實際開發中的場景。html
Class Component 內部複雜的生命週期函數使得咱們組件內部的 componentDidMount
愈來愈複雜和臃腫,獨立組件動輒上千行代碼;組件嵌套層級愈來愈深,組件之間的狀態複用也變得很是困難。前端
Hook 無疑是可選的,他不會對現有項目形成任何衝擊和破壞,社區對於它的優點也有過不少討論;不過目前官方也沒有計劃移除 Class,而是推薦漸進式的去使用 Hook,在一些新增的組件中優先選用 Hook。那麼咱們想要在原有以 Class Component 爲主的項目中開始使用 Hook,與原有的 Class Component 必然會產生交互,是否是須要將這些 Class Component 重寫爲 Hook 呢?java
將部分複雜的 Class Component 逐步重寫爲 Hook 應該排在項目迭代的中長期計劃中,若是想要在一個迭代中進行大量改造,帶來的巨大成本和反作用也是沒法估量的。react
那麼短時間內咱們就繞不開 Hook 與 Class 組件的混合使用。數組
先簡單介紹一下兩種組件的基本寫法:antd
Class Components:類組件的寫法dom
export default class ShowHook extends Component { return ( <h1>Hello Hook!</h1> ); }
Function Components:Hook 組件的寫法函數
function ShowHook (props){ return ( <h1>Hello Hook!</h1> ); }
混合使用就難以免的須要進行通訊和參數傳遞,下面我用一個簡單的處理模塊顯示隱藏的功能組件 ShowHook
做爲一個例子,介紹三種是比較常見混合使用的方式,先來看一下效果:post
Render props 中來自父組件的 props children 是一個 Function
,咱們能夠將子組件的內部變量經過函數傳遞至父組件,達到通訊的目的。
// 子組件 SayHello.js import React, { useState } from 'react'; function sayHello({ children }) { const [visible, changeVisible] = useState(false); const jsx = visible && ( <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1> ); return children({ changeVisible, jsx }); } export default sayHello;
父組件獲取到 changeVisible
方法以後就能方便的控制 visible
的狀態。
// 父組件 ShowHook.js import React, { Component, Fragment } from 'react'; import SayHello from '../components/SayHello'; export default class ShowHook extends Component { render() { return ( <SayHello> {({ changeVisible, jsx }) => { return ( <React.Fragment> <button onClick={() => changeVisible(true)}> showChild </button> {jsx} </React.Fragment> ); }} </SayHello> ); } }
props.children
經常使用的類型是字符串、對象甚至數組;但其實咱們也能夠傳入一個函數,只要最終能返回出DOM 樹便可;Render props 是將 Render 部分抽離出來做爲函數傳入子組件;它主要的做用是將 state 部分抽成組件,實現 state 的複用。
// 封裝子組件 function Mouse (props) { const [position, setPosition] = useState({x: 0,y: 0}); const handleMouseMove = (e) => { setPosition({ x: e.clientX, y: e.clientY }) } return ( <div onMouseMove={handleMouseMove}> {this.props.children(position)} </div> ) } // 使用場景 1:圖片位置跟隨鼠標 class Cat1 extends React.Component { render() { return ( <Mouse> {(position) => <img src="/cat.jpg" style={{ position: 'absolute', left: position.x, top: position.y }} /> } </Mouse> ) } } // 使用場景 2:頁面展現鼠標座標 class Cat2 extends React.Component { render() { return ( <Mouse> {(position) => <h1>x: {position.x} y: {position.y}</h1> } </Mouse> ) } }
上面使用了 React 官方文檔中的例子進行改寫,具體效果以下:
場景 1:
場景 2:
HOC (Higher-Order Components) 是另外一種提升代碼複用率的常見技巧,它接收一個組件做爲參數,最終返回出一個新的組件。
下面的方法使得 button
控制任意組件顯示隱藏的功能被封裝爲高階組件,得以複用,而且 setVisible
方法也能被傳遞到 Class Component
中。
// 高階組件 SayHello.js import React, { useState, Fragment } from 'react'; const sayHello = (Component) => { return (props) => { const [visible, setVisible] = useState(false); return ( <React.Fragment> <button onClick={() => setVisible(true)}> showChild </button> {visible && <Component changeVisible={setVisible} visible={visible} />} </React.Fragment> ); }; }; export default sayHello;
在外部 Class Component 中咱們能夠定製受內部顯示/隱藏控制的組件,而且使用高階組件中向外傳遞的 props 。
// ShowHook.js import React, { Component } from 'react'; import SayHello from '../components/SayHello'; class ShowHook extends Component { render() { const { changeVisible } = this.props; return ( <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1> ); } } export default SayHello(ShowHook);
HOC 在實際使用中是將一些反作用函數、公用方法做爲組件抽取出來,從而提高複用率;咱們能夠把父組件的render
部分改成一個彈窗,或任意內容使得子組件獲得複用,例如:
// 複用高階組件 SayHello import React, { Component } from 'react'; import SayHello from '../components/SayHello'; import { Modal } from 'antd'; class ShowModal extends Component { render() { const { changeVisible, visible } = this.props; return ( <Modal title="Basic Modal" visible={visible} onOk={() => changeVisible(false)} onCancel={() => changeVisible(false)} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> ); } } export default SayHello(ShowHook);
這樣就能夠輕鬆的控制彈窗的顯示隱藏;實際效果以下:
Ref 轉發是一項將 Ref 自動地經過組件傳遞到其一子組件的技巧。對於大多數應用中的組件來講,這一般不是必需的,但其對某些組件,尤爲是可重用的組件庫是頗有用的。
它能夠將子組件的方法暴露給父組件使用。
// 父組件 ShowHook.js import React, { Component } from 'react'; import SayHello from './SayHello'; export default class ShowHook extends Component { showChild = () => { console.log(this.child); //能夠看到 changeVisible 方法被掛載到了 this.child 下 // {changeVisible: f()} this.child.changeVisible(true); } // 將子組件暴露出來的對象掛載到 child onRef = (ref) => { this.child = ref; } render() { return ( <React.Fragment> <button onClick={this.showChild}>showChild</butotn> <SayHello ref={this.onRef} /> </React.Fragment> ); } } // 子組件 SayHello.js import React, { useState, useImperativeHandle, forwardRef } from 'react'; function SayHello(props, ref) { const [visible, changeVisible] = useState(false); // 暴露的子組件方法,給父組件調用 useImperativeHandle(ref, () => { return { changeVisible, }; }); return visible && ( <h1 onClick={() => changeVisible(false)}> Hello Hook! </h1> ); } export default forwardRef(SayHello);
上面例子中封裝了一個子組件,任意一個使用了該子組件的地方均可以控制它的狀態。
目前 Hooks 尚不具有完整的 Class Component 的功能,一些不經常使用的生命週期函數尚不支持,例如:getSnapshotBeforeUpdate
, getDerivedStateFromError
以及 componentDidCatch
,但官方已將他們 排入計劃內,相信不久以後就會獲得支持;將來 Hooks 可能將成爲 React Components 的首選,在現階段就讓咱們愉快的混合使用吧。
How to Use React Hooks in Class Components
政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 40 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。
若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com