React 高阶组件HOC使用总结
生活随笔
收集整理的這篇文章主要介紹了
React 高阶组件HOC使用总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
HOC(高階組件)
/*HOC(高階組件): 接收一個組件,返回包裝后的組件(增強組件)- 不是React API- 是一種設計模式,類似于裝飾器模式- ≈ Mixin && > Minxinconst 包裝后的組件 = 高階組件(被包裝的組件);// e.g. const Wrapper = withRouter(NavBar);高階組件會把所有接收到的props,傳遞給被包裝的組件(透傳)ref 和 key 類似,不是一個prop,所以不會透傳,ref會綁定到外層的包裝容器上 | 解決方法可以參考下面的 <<處理ref>> * */怎樣包裝組件?
/*怎樣包裝組件?第一種: 普通包裝export時就包裝import React from 'react';import Hoc from './Hoc';class Header extends React.Component {render() {return <span>{ this.props.count }</span>}};export default Hoc(Header);==========import后再包裝:import Header from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);const Home = () => {return (<div><EnhanceHeader count={1} /></div>)}第二種: 裝飾器包裝,只能在類組件中使用import React from 'react';import Hoc from './Hoc';@Hocexport default class Header extends React.Component {render() {return <span>{ this.props.count }</span>}};=======@Hocclass Header extends React.Component {render() {return <span>{ this.props.count }</span>}};export default Header; * */定義一個簡單的HOC
/*定義一個簡單的HOC,接收一個組件,返回一個組件import React from 'react';// 返回類組件export default function Hoc(WrappedComponent) {/*return class extends React.Component {}- 在 React Developer Tools 中展示的名字是 Componentreturn class Wrapper extends React.Component {}- 在 React Developer Tools 中展示的名字是 Wrapper*\return class extends React.Component {render() {return <WrappedComponent {...this.props} />;}};}// 返回函數式組件export default function Hoc(WrappedComponent) {/*return function(props) {}- 在 React Developer Tools 中展示的名字是 Anonymousreturn function Wrapper(props) {}- 在 React Developer Tools 中展示的名字是 Wrapper*\return function Wrapper(props) {return <WrappedComponent {...props} />;};} * */給Hoc傳參
/*給Hoc傳參// Hoc,可以接受任意參數export default function Hoc(WrappedComponent, title, user, data) {return class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};};// 包裝時傳參const EnhanceHeader = Hoc(Header, 'title', { name: '霖'}, [1, 2, 3]); * */Hoc嵌套
/*Hoc嵌套,函數柯里化的原理// Hoc1: 給組件添加title屬性export default function Hoc1(WrappedComponent, title) {return class extends React.Component {render() {return <WrappedComponent title={title} {...this.props} />}};};// Hoc2: 修改組件的顯示內容export default function Hoc2(WrappedComponent, content) {return class extends WrappedComponent { // 這里用了反向繼承render() {const elementTree = super.render(); // React用Js對象來模擬Dom樹結構,可以通過修改Js對象的屬性來操縱數據console.log(elementTree); // 不太了解里面的結構可以打印出來 + 官網cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的內容已被劫持: ${content}` });return newElementTree;}};};// 被包裹的組件export default class Header extends React.Component {render() {const { title } = this.props;return (<span title={title}>默認內容</span>)}};// 使用import Hoc1 from './Hoc1';import Hoc2 from './Hoc2';/*包裝過程1. const Wrapper = Hoc2(Header, '內容');2. Hoc1(Wrapper)**const EnhanceHeader = Hoc1(Hoc2(Header, '內容'), '標題');export default function Home() {return (<div><EnhanceHeader /></div>);};* */處理ref
/*處理refe.g. Hoc1(Hoc2(Content))<Content ref={myRef} /> 給Content綁定的ref會綁定到Hoc1上,且不會繼續向下傳遞第一種方法 React.forwardRef ===============在 Hoc1外面 用React.forwardRef()對ref做處理,用props來傳遞ref0. 在高階組件外面包裹forwardRef,攔截獲取ref,增加一個props(xxx={ref}),真實組件通過props.xxx獲取1. 使用時傳 ref={XXXX} // 和第二種方法不同的地方2. 用forwardRef的第二個參數獲取 ref3. 增加一個新的props,用來向下轉發ref e.g. forwardedRef={ref}4. 真實組件中綁定 ref={props.forwardedRef}const Home = (props) => {const connectRef = useRef(null);return (<div><Content ref={connectRef} /></div>);};// 被包裝組件const Content = (props) => {return (<div><input type="password" ref={props.forwardedRef} /></div>);};// forwardRef的第二個入參可以接收ref,在Hoc外層對ref做處理export default React.forwardRef((props, ref) => {const Wrapper = React.memo(Content); // Hoc// forwardRef包裹的是Wrapper// 需要在Wrapper中把ref向下傳遞給真實組件// Wrapper中增加一個props屬性,把ref對象作為props傳給子組件return <Wrapper {...props} forwardedRef={ref} />;});第二種方法 ==========0. 使用時就用一個props來保存ref1. 使用時傳 xxx={ref} // 和第一種方法的不同點2. 真實組件中綁定 ref={props.xxx}const Home = (props) => {const connectRef = useRef(null);return (<div><Content forwardedRef={connectRef} /></div>);};// 定義高階組件export const Hoc = (WrappedComponent) => {class Wrapper extends React.Component {render() {return <WrappedComponent {...props} />}}}// 被包裝的組件const Content = (props) => {return (<div><input type="password" ref={props.forwardedRef} /></div>);};// 包裝過程export default Hoc(Content);* */使用被包裝組件的靜態方法
/*使用被包裝組件的靜態方法// 被包裝組件,增加靜態屬性和方法export default class Header extends React.Component {static displayName = 'header';static showName = () => {console.log(this.displayName);};render() {return <span>header</span>}};// HOCexport default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};};===========// Hoc包裝后的組件拿不到靜態方法import Header from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);export default function Home() {console.log(EnhanceHeader.displayName); // undefinedEnhanceHeader.showName(); // undefinedreturn <EnhanceHeader />}=============// 解決方法1:拷貝靜態方法到HOC上export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {static displayName = WrappedComponent.displayName; // 必須知道被包裝組件中有什么靜態方法static showName = WrappedComponent.showName;render() {return <WrappedComponent {...this.props} />}};};==============// 解決方法2:自動拷貝所有靜態屬性和方法import React from 'react';import hoistNonReactStatic from 'hoist-non-react-statics';export default function Hoc(WrappedComponent) {class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};hoistNonReactStatic(Wrapper, WrappedComponent);return Wrapper;};==============// 解決方法3:導出組件時,額外導入靜態屬性和方法class Header extends React.Component {render() {return <span>header</span>}};const displayName = 'header';function showName() {console.log(Header.displayName);};Header.displayName =displayName;Header.showName = showName;export default Headerexport { displayName, showName }// 導入時import Header, { displayName, showName } from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);export default function Home() {console.log(displayName); // headershowName(); // headerreturn <EnhanceHeader />} * */攔截傳給被包裝組件的props,對props進行增刪改
/*攔截傳給被包裝組件的props,對props進行增刪改export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {// 過濾一些僅在當前Hoc中使用的props,不進行不必要的透傳const { forMeProps, forOtherProps } = this.props;// 在該HOC內部定義,需要注入到被包裝組件的額外的屬性或方法const injectProps = some-state-or-method; // 通常是state或實例方法// 為被包裝組件傳遞上層的props + 額外的propsreturn (<WrappedComponentinjectProps={injectProps} // 傳遞需要注入的額外props{...forOtherProps} // 透傳與后續相關的props/>)}}}e.g.Hoc接收一個額外的props 'dealUpper',如果為true,將data轉換成大寫dealUpper只在該Hoc中使用,所以沒必要傳給被包裝的組件// HOCexport default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {const { dealUpper, ...forOtherProps } = this.props;const { data } = forOtherProps;if (dealUpper) {Object.assign(forOtherProps, {data: data.toUpperCase()})}return <WrappedComponent {...forOtherProps} />}};};// 導出Hoc包裝后的增強組件import React from 'react';import Hoc from './Hoc1';class Header extends React.Component {render() {console.log(this.props); // { data: 'ABC' }return <span>{this.props.data}</span>}};export default Hoc(Header); // 導出包裝后的增強組件// 導入使用import Header from './header';const Home = () => {return <Header data={'abc'} dealUpper />} * */用HOC提取一些復雜的公共邏輯,在不同組件中擴展不同的功能
/*用HOC提取一些復雜的公共邏輯,在不同組件中擴展不同的功能import React from 'react';export const Hoc = (WrappedComponent, namespace) => {class Wrapper extends React.Component {state = {data: []}// 抽離的相同請求方法componentDidMount = () => {const { dispatch } = this.props;dispatch({type: `${namespace}/queryData`, // 動態請求不同的storepayload: {},callback: res => {if (res) {this.setState({data: res.data})}}})}render() {return <WrappedComponent { ...this.props } data={this.state.data} />}}}// 包裝A組件import Hoc from './Hoc';const A = ({ data }) => {... 省略請求數據的邏輯return (data.map(item => item));}export default MyHoc(A, 'a');// 包裝B組件import Hoc from './Hoc';const B = ({ data }) => {... 省略請求數據的邏輯return (<ul>{data.map((item, index) => {return <li key={index}><{item}/li>}}</ul>)}export default Hoc(B, 'b'); * */讓不受控組件變成受控組件
/*讓不受控組件變成受控組件// Hoc組件export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {state = {value: ''};onChange = (e) => {this.setState({value: e.target.value})};render() {const newProps = {value: this.state.value,onChange: this.onChange};return <WrappedComponent {...this.props} {...newProps} />}};};// 普通組件class InputComponent extends React.Component {render() {return <input {...this.props} />}}// 包裝export default Hoc(InputComponent); * */反向繼承
/*反向繼承(在Hoc中使用被包裝組件內部的狀態和方法)- 反向繼承的組件要是類組件,函數組件不行export const Hoc = (WrappedComponent) => {class Wrapper extends WrappedComponent { // super ≈ WrappedComponent里面的thisrender() {if (!this.props.data) {return <span>loading....</span>} else {return super.render() // 調用被包裝組件的render()方法}}}}====export default function Hoc(WrappedComponent) {return class extends WrappedComponent {render() {const elementTree = super.render(); // React用Js對象來模擬Dom樹結構,可以通過修改Js對象的屬性來操縱數據console.log(elementTree); // 不太了解里面的結構可以打印出來 + 官網cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的內容已被劫持` });return newElementTree;}};}; * */渲染劫持
/*渲染劫持e.g. 控制組件是否渲染(可以做全局的loading效果,沒有數據時顯示loading...)// 基本的實現export const LoadingHoc = (WrappedComponent) => {class Wrapper extends React.Component {render() {if (!this.props.data) {return <span>loading....</span>} else {return <WrappedComponent {...this.props} />}}}}// 用反向繼承實現export const LoadingHoc = (WrappedComponent) => {class Wrapper extends WrappedComponent { // super ≈ WrappedComponent里面的thisrender() {if (!this.props.data) {return <span>loading....</span>} else {return super.render() // 調用被包裝組件的render()方法}}}}======e.g. 劫持渲染的內容export default function Hoc2(WrappedComponent) {return class extends WrappedComponent { // 這里用了反向繼承render() {const elementTree = super.render(); // React用Js對象來模擬Dom樹結構,可以通過修改Js對象的屬性來操縱數據console.log(elementTree); // 不太了解里面的結構可以打印出來 + 官網cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的內容已被劫持` });return newElementTree;}};}; * */配置包裝名
/*配置包裝名:在調試工具 React Developer Tools 中更容易被找到e.g. 高階組件為Hoc,被包裝組件為WrappedComponent, 顯示的名字應該是 Hoc(WrappedComponent)// 返回類組件export default function Hoc(WrappedComponent) {return class extends React.Component {/*沒有在Hoc中定義 static displayName = 'XXX';- React Developer Tools 中展示的名字是 Anonymous沒有在被包裝組件中定義 static displayName = 'XXX';- React Developer Tools 中展示的名字是 undefined Hoc在被包裝組件中定義 static displayName = 'header';- React Developer Tools 中展示的名字是 header Hoc*\static displayName = `Hoc(${WrappedComponent.displayName});render() {return <WrappedComponent {...this.props} />;}};}// 返回函數式組件export default function Hoc(WrappedComponent) {/*return function(props) {}- 在 React Developer Tools 中展示的名字是 Anonymousreturn function Wrapper(props) {}- 在 React Developer Tools 中展示的名字是 Wrapper*return function Wrapper(props) {return <WrappedComponent {...props} />;};}=======export default function Hoc(WrappedComponent) {const Wrapper = (props) => {return <WrappedComponent {...props} />;};/*沒有在被包裝組件中定義 static displayName = 'XXX';- React Developer Tools 中展示的名字是 undefined Hoc在被包裝組件中定義 static displayName = 'header';- React Developer Tools 中展示的名字是 header Hoc*\Wrapper.displayName = `Hoc(${WrappedComponent.displayName})`;return Wrapper;}=====// 被包裹組件export default class Header extends React.Component {static displayName = 'header';render() {return <span>{ this.props.count }</span>}};* */不要在render中使用HOC
/*不要在render中使用HOCe.g.export default class Home extends React.Component {render() {// 每次render都會創建一個新的Wrapper// Wrapper1 !== Wrapper2// 導致高階組件會卸載和重新掛載,狀態會丟失(e.g. checkbox的選中丟失 | state被清空)× const Wrapper = Hoc(WrappedComponent);return <Wrapper />}}=========√ const Wrapper = myHoc(WrappedComponent);export default class Home extends React.Component {render() {return <Wrapper />}} * */Hoc的渲染順序
/*Hoc的渲染順序Hoc(Header)componentDidMount: Header -> HOCcomponentWillUnMount: HOC -> Header * */HOC 和 Mixin
/*HOC 和 MixinHOC- 屬于函數式編程思想- 被包裹組件感知不到高階組件的存在- 高階組件返回的組件會在原來的基礎上的到增強Mixin- 混入模式,會在被包裝組件上不斷增加新的屬性和方法- 被包裹組件可感知- 需要做處理(命名沖突、狀態維護) * */總結
以上是生活随笔為你收集整理的React 高阶组件HOC使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言-函数案例-计算圆周长
- 下一篇: WTL 绘制 圆角对话框 自绘对话框