亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

React將組件作為參數(shù)進(jìn)行傳遞的3種方法實(shí)例

 更新時(shí)間:2022年07月24日 10:13:08   作者:martinzai  
其實(shí)react組件之間傳遞參數(shù)是比較簡(jiǎn)單的,組件傳入?yún)?shù)的一種方式,下面這篇文章主要給大家介紹了關(guān)于React將組件作為參數(shù)進(jìn)行傳遞的3種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在日常的開(kāi)發(fā)中,開(kāi)發(fā)通用組件的機(jī)會(huì)其實(shí)并不多,尤其是在各種組件庫(kù)已經(jīng)遍地都是的情況下。而作為一個(gè)通用組件庫(kù)的使用者,經(jīng)常會(huì)看到把 React 組件作為參數(shù)傳遞下去的場(chǎng)景,每當(dāng)這個(gè)時(shí)候,其實(shí)或多或少都會(huì)有一些疑問(wèn),比如:有些組件傳遞下去的是組件名,而有些組件傳遞下去的是一個(gè)箭頭函數(shù)返回一個(gè)組件,而有些直接傳遞一個(gè) jsx 創(chuàng)建好的元素,這些傳遞方案的適用場(chǎng)景如何,有什么不同,是否會(huì)導(dǎo)致組件的 memo 失效,是否會(huì)引發(fā)組件的不必要渲染?

本文是筆者在閱讀了 antd、mui, react-select 的 api 之后,結(jié)合自己日常業(yè)務(wù)中使用的組件 api 格式,對(duì)傳遞一個(gè)組件作為 React 組件參數(shù)的方式的思考和總結(jié),如果有寫(xiě)的不到位的,歡迎補(bǔ)充和指點(diǎn)。

大體來(lái)講,傳遞組件的方式,分為三種:

  • 傳遞 jsx 創(chuàng)建好的元素
  • 傳遞組件本身
  • 傳遞返回 jsx 創(chuàng)建好的元素的函數(shù)

下文也主要展開(kāi)介紹這三種方式并結(jié)合實(shí)際場(chǎng)景對(duì)比這三種方案。

方式一:直接傳遞 jsx 創(chuàng)建好的元素

在 antd 的組件 api 中,最常見(jiàn)的方式便是這個(gè)方法,以 button 為例,有一個(gè) icon 參數(shù)便是允許使用者傳遞一個(gè)經(jīng)過(guò) jsx 創(chuàng)建好的元素。簡(jiǎn)化后的示例如下:

function DownloadOutlined() {
    return /* icon 的實(shí)現(xiàn)*/;
} 

function Button({ icon, children }) {
    return <button>
        {icon}
        {children}
    </button>
}

function App() {
    return <Button icon={<DownloadOutlined />}>test</Button>
}

可以看出來(lái),icon 直接傳遞了一個(gè) jsx 創(chuàng)建好的組件,從而滿足了用戶自定義 icon 的需求。

相比于通過(guò)字符串枚舉內(nèi)置 icon, 給了用戶更大的定制空間。

方式二:直接傳遞組件本身

這一用法在 antd 中很少出現(xiàn),在 react-select 中比較常見(jiàn)。

這里為了方便還是以 Button 為例,修改下上文的 Button 組件,將其參數(shù)改為傳遞 DownloadOutlined 而非經(jīng)過(guò) jsx 創(chuàng)建好的元素 <DownloadOutlined />

function DownloadOutlined() {
    return /* icon 的實(shí)現(xiàn)*/;
} 

function Button({ icon: Icon, children }) {
    return <button>
    // 渲染方式進(jìn)行了改變
    <Icon />
    {children}
    </Button>
}

function App() {
    return <Button icon={DownloadOutlined}>test</Button>
}

通過(guò)直接傳遞組件本身的方式,也可將其傳遞給子組件進(jìn)行渲染,當(dāng)然,子組件渲染的地方也改成了 <Icon /> 而非上文的 {icon}。ps: 上文中由于 jsx 語(yǔ)法要求,將 icon 變量名改成了首字母大寫(xiě)的 Icon。

方式三:傳遞一個(gè)返回組件的函數(shù)

這一用法用 Button 示例改寫(xiě)如下:

function DownloadOutlined() {
    return /* icon 的實(shí)現(xiàn)*/;
} 

function Button({ icon, children }) {
    return <button>
    // 渲染方式進(jìn)行了改變
    {icon()}
    {children}
    </Button>
}

function App() {
    return <Button icon={() => <DownloadOutlined />}>test</Button>
}

在這一例子中,由于傳遞的是個(gè)函數(shù),那么返回值在渲染時(shí),改成執(zhí)行函數(shù)即可。

三種方案的對(duì)比

上文中分別介紹了這三種方案的實(shí)現(xiàn)方法,從結(jié)果來(lái)看,三種方案都能滿足傳遞組件作為組件參數(shù)的場(chǎng)景。

但是在實(shí)際的場(chǎng)景中,往往不會(huì)這么簡(jiǎn)單,往往有更多需要考慮的情況。

情況一: 考慮是否存在不必要的渲染?

三種方案下,當(dāng)父組件發(fā)生渲染時(shí),Button 組件是否會(huì)發(fā)生不必要的渲染。示例如下:

import React, { useState } from 'react';

function DownloadOutlined() {
  return <span>icon</span>;
}

const Button1 = React.memo(({ icon, children }) => {
  console.log('button1 render');

  return (
    <button>
      {icon}
      {children}
    </button>
  );
});

const Button2 = React.memo(({ icon: Icon, children }) => {
  console.log('button2 render');

  return (
    <button>
      <Icon />
      {children}
    </button>
  );
});

const Button3 = React.memo(({ icon, children }) => {
  console.log('button3 render');
  return (
    <button>
      {icon()}
      {children}
    </button>
  );
});

export default function App() {
  const [count, setCount] = useState(0);
  console.log('App render');

  return (
    <>
      <Button1 icon={<DownloadOutlined />}>button1</Button1>
      <Button2 icon={DownloadOutlined}>button2</Button2>
      <Button3 icon={() => <DownloadOutlined />}>button3</Button3>
      <button onClick={() => setCount((pre) => pre + 1)}>render</button>
    </>
  );
}

在該示例中,點(diǎn)擊 render button,此時(shí),期望的最小渲染應(yīng)該是僅僅渲染 app 組件即可,Button1 - Button3 由于并未依賴 count 的變化,同時(shí) Button1 - Button3 都通過(guò) React.memo 進(jìn)行包裹,期望的是組件不進(jìn)行渲染。

實(shí)際輸出如下:

可以看出,Button1 和 Button3 均進(jìn)行了渲染,這是由于這兩種方案下,icon的參數(shù)發(fā)生了變化,對(duì)于 Button1, <DownloadOutlined />, 本質(zhì)是 React.createElement(DownloadOutlined), 此時(shí)將會(huì)返回一個(gè)新的引用,就導(dǎo)致了 Button1 參數(shù)的改變,從而使得其會(huì)重新渲染。而對(duì)于 Button3,就更加明顯,每次渲染后返回的箭頭函數(shù)都是新的,自然也會(huì)引發(fā)渲染。而只有方案二,由于返回的始終是組件的引用,故不會(huì)重新渲染。

要避免(雖然實(shí)際中,99%的場(chǎng)景都不需要避免,也不會(huì)有性能問(wèn)題)這種情況,可以通過(guò)加 memo 解決。改動(dòng)點(diǎn)如下:

export default function App() {
  const [count, setCount] = useState(0);
  console.log('App render');

  const button1Icon = useMemo(() => {
      return <DownloadOutlined />;
  }, []);

  const button3Icon = useCallback(() => {
      return () => <DownloadOutlined />;
  }, []);

  return (
    <>
      <Button1 icon={butto1Icon}>button1</Button1>
      <Button2 icon={DownloadOutlined}>button2</Button2>
      <Button3 icon={button3Icon}>button3</Button3>
      <button onClick={() => setCount((pre) => pre + 1)}>render</button>
    </>
  );
}

通過(guò) useMemo, useCallback包裹后,即可實(shí)現(xiàn) Button1, Button3 組件參數(shù)的不變,從而避免了多余的渲染。相比之下,目前看,直接傳遞組件本身的方案寫(xiě)法似乎更為簡(jiǎn)單。

實(shí)際的場(chǎng)景中,Icon 組件往往不會(huì)如此簡(jiǎn)單,往往會(huì)有一些參數(shù)來(lái)控制其比如顏色、點(diǎn)擊行為以及大小等等,此時(shí),要將這些參數(shù)傳遞給 Icon 組件,這也是筆者想要討論的:

情況二:需要傳遞來(lái)自父組件(App)的參數(shù)的情況。

在現(xiàn)有的基礎(chǔ)上, 以傳遞 size 到 Icon 組件為例,改造如下:

import React, { useState, useMemo, useCallback } from 'react';

// 增加 size 參數(shù), 控制 icon 大小
function DownloadOutlined({ size }) {
  return <span style={{ fontSize: `${size}px` }}>icon</span>;
}

// 無(wú)需修改
const Button1 = React.memo(({ icon, children }) => {
  console.log('button1 render');

  return (
    <button>
      {icon}
      {children}
    </button>
  );
});

// 增加 iconProps,來(lái)傳遞給 Icon 組件
const Button2 = React.memo(({ icon: Icon, children, iconProps = {} }) => {
  console.log('button2 render');

  return (
    <button>
      <Icon {...iconProps} />
      {children}
    </button>
  );
});

// 無(wú)需修改
const Button3 = React.memo(({ icon, children }) => {
  console.log('button3 render');
  return (
    <button>
      {icon()}
      {children}
    </button>
  );
});

export default function App() {
  const [count, setCount] = useState(0);
  const [size, setSize] = useState(12);
  console.log('App render');
  
  // 增加size依賴
  const button1Icon = useMemo(() => {
    return <DownloadOutlined size={size} />;
  }, [size]);
  
  // 增加size依賴
  const button3Icon = useCallback(() => {
    return <DownloadOutlined size={size} />;
  }, [size]);

  return (
    <>
      <Button1 icon={button1Icon}>button1</Button1>
      <Button2 icon={DownloadOutlined} iconProps={{ size }}>
        button2
      </Button2>
      <Button3 icon={button3Icon}>button3</Button3>
      <button onClick={() => setCount((pre) => pre + 1)}>render</button>
      <button onClick={() => setSize((pre) => pre + 1)}>addSize</button>
    </>
  );
}

通過(guò)上述改動(dòng),可以發(fā)現(xiàn),當(dāng)需要從 App 組件中,向 Icon 傳遞參數(shù)時(shí),Button1 和 Button3 組件本身不需要做任何改動(dòng),僅僅需要修改 Icon jsx創(chuàng)建時(shí)的參數(shù)即可,而 Button2 的 Icon 由于渲染發(fā)生在內(nèi)部,故需要額外傳遞 iconProps 作為參數(shù)傳遞給 Icon。與此同時(shí),render按鈕點(diǎn)擊時(shí),由于 iconProps 是個(gè)引用類型,導(dǎo)致觸發(fā)了 Button2 的額外渲染,當(dāng)然可以通過(guò) useMemo 來(lái)控制,此處不再贅述。

接下來(lái)看情況三,當(dāng)子組件(Button1 - button3)需要傳遞它自身內(nèi)部的狀態(tài)到 Icon 組件中時(shí),需要做什么改動(dòng)。

設(shè)想一個(gè)虛構(gòu)的需求, Button1 - Button3 組件內(nèi)部維護(hù)了一個(gè)狀態(tài),count,也就是每個(gè)組件點(diǎn)擊的次數(shù),而 DownloadOutlined 也接收一個(gè)參數(shù),count, 隨著 count 的變化,他的顏色會(huì)從 rbg(0, 0, 0) 變化為 rgb(count, 0, 0)

DownloadOutlined 改動(dòng)如下:

// 增加 count 參數(shù),控制 icon 顏色
function DownloadOutlined({ size = 12, count = 0 }) {
  console.log(count);
  return (
    <span style={{ fontSize: `${size}px`, color: `rgb(${count}, 0, 0)` }}>
      icon
    </span>
  );
}

Button2 的改造(Button1放在最后)如下:

const Button2 = React.memo(({ icon: Icon, children, iconProps = {} }) => {
  console.log('button2 render');
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(pre => pre + 40)}>
      {/* 將count參數(shù)注入即可 */}
      <Icon {...iconProps} count={count} />
      {children}
    </button>
  );
});

Button3的改造如下:

const Button3 = React.memo(({ icon, children }) => {
  console.log('button3 render');
  const [count, setCount] = useState(0);

  return (
    // 此處為了放大顏色的改變,點(diǎn)擊一次加 40
    <button onClick={() => setCount(pre => pre + 40)}>
      {/* 將 count 作為參數(shù)傳遞給 icon 函數(shù) */}
      {icon({count})}
      {children}
    </button>
  );
});

相應(yīng)的,App 組件傳入也需要做改動(dòng)

export default function App() {
  /* 省略 */
  
  const button3Icon = useCallback((props) => {
    // 接收參數(shù)并將其傳遞給icon組件
    return <DownloadOutlined size={size} {...props} />;
  }, [size]);

  /* 省略 */
}

而對(duì)于 button1, 由于 icon 渲染的時(shí)機(jī),是在 App 組件中,而在 App 組件中,獲取 Button1 組件內(nèi)部的狀態(tài)并不方便(可以通過(guò) ref, 但是略顯麻煩)。此時(shí)可以借助 React.cloneElement api來(lái)新建一個(gè) Icon 組件并將子組件參數(shù)注入,改造如下:

const Button1 = React.memo(({ icon, children }) => {
  console.log('button1 render');
  const [count, setCount] = useState(0);
  // 借助 cloneElement 向icon 注入?yún)?shù)
  const newIcon = React.cloneElement(icon, {
    count,
  });

  return (
    <button onClick={() => setCount((pre) => pre + 40)}>
      {newIcon}
      {children}
    </button>
  );
});

從這個(gè)例子可以看出,如果傳入的組件(icon),需要獲取即將傳入組件(Button1, Button2, Button3)內(nèi)部的狀態(tài),那么直接傳遞 jsx 創(chuàng)建好的元素,并不方便,因?yàn)樵诟附M件(App)中獲取子組件(Button1)內(nèi)部的狀態(tài)并不方便,而直接傳遞組件本身,和傳遞返回 jsx 創(chuàng)建元素的函數(shù),前者由于元素真正的創(chuàng)建,就是發(fā)生在子組件內(nèi)部,故可以方便的獲取子組件狀態(tài),而后者由于是函數(shù)式的創(chuàng)建,通過(guò)簡(jiǎn)單的參數(shù)傳遞,即可將內(nèi)部參數(shù)傳入 icon 中,從而方便的實(shí)現(xiàn)響應(yīng)的需求。

總結(jié)

本文先簡(jiǎn)單介紹了三種將組件作為參數(shù)傳遞的方案:

  • 傳遞 jsx 創(chuàng)建好的元素: icon = {<Icon />}
  • 傳遞組件本身: icon={Icon}
  • 傳遞返回 jsx 創(chuàng)建好的元素的函數(shù): icon={() => <Icon />}

接下來(lái),從三個(gè)角度對(duì)其進(jìn)行分析:

  • 是否存在不必要的渲染
  • Icon 組件需要接收來(lái)自父組件的參數(shù)
  • Icon 組件需要接收來(lái)自子組件的參數(shù)

其中,三種方案,在不做 useMemo, useCallback 這樣的緩存情況下,直接傳遞組件本身,由于引用不變,可以直接避免非必要渲染,但是當(dāng)需要接收來(lái)自父組件的參數(shù)時(shí),需要開(kāi)辟額外的字段 iconProps 來(lái)接收父組件的參數(shù),在不做緩存的情況下,由于參數(shù)的對(duì)象引用每次都會(huì)更新從而也存在不必要渲染的情況。當(dāng)然,這種不必要的渲染,在絕大部分場(chǎng)景下,并不會(huì)存在性能問(wèn)題。

考慮了來(lái)自父組件的傳參后,除了方案二直接傳遞組件本身的方案需要對(duì)子組件增加 iconProps 之外,其余兩個(gè)方案由于 jsx 創(chuàng)建組件元素的寫(xiě)法本身就在父組件中,只需稍作改動(dòng)即可將參數(shù)攜帶入 Icon 組件中。

而當(dāng)需要接收來(lái)自子組件的參數(shù)場(chǎng)景下,方案一顯得略有不足,jsx 的創(chuàng)建在父組件已經(jīng)創(chuàng)建好,子組件中需要注入額外的參數(shù)相對(duì)麻煩(使用 cloneElement 實(shí)現(xiàn)參數(shù)注入)。而方案三由于函數(shù)的執(zhí)行時(shí)機(jī)是在子組件內(nèi)部,可以很方便的將參數(shù)通過(guò)函數(shù)傳參帶入 Icon 組件,可以很方便的滿足需求。

從實(shí)際開(kāi)發(fā)組件的場(chǎng)景來(lái)看,被作為參數(shù)傳遞的組件需要使用子組件內(nèi)部參數(shù)的,一般通過(guò)方案三傳遞函數(shù)的方案來(lái)設(shè)計(jì),而不需要子組件內(nèi)部參數(shù)的,方案一二三均可,實(shí)際的開(kāi)銷幾乎沒(méi)有差異,只能說(shuō)方案一寫(xiě)法較為簡(jiǎn)單,也是 antd 的 api 中最常見(jiàn)的用法。而方案三,多見(jiàn)于需要子組件內(nèi)部狀態(tài)的情況,比如 antd 的面包屑 itemRender,F(xiàn)orm.list的 children 的渲染,通過(guò)函數(shù)注入?yún)?shù)給被作為參數(shù)傳遞的組件方便靈活的進(jìn)行渲染。

最后,由于筆者之前寫(xiě)過(guò)一段時(shí)間vue,不免還是想到了 vue 中 slot 的寫(xiě)法,說(shuō)實(shí)話,還是回去翻了下文檔,其實(shí)就是方案一和方案三的合集,由于slot本身是在父組件渲染的,所以直接具備父組件的作用域,能夠訪問(wèn)父組件的狀態(tài),需要注入父組件參數(shù)的,直接在插槽的組件中使用即可,而作用域插槽便是提供子組件的作用域,使插槽中的組件可以獲取到子組件的參數(shù)。

到此這篇關(guān)于React將組件作為參數(shù)進(jìn)行傳遞的3種方法的文章就介紹到這了,更多相關(guān)React組件作參數(shù)傳遞內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React為什么需要Scheduler調(diào)度器原理詳解

    React為什么需要Scheduler調(diào)度器原理詳解

    這篇文章主要為大家介紹了React為什么需要Scheduler調(diào)度器原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React?Streaming?SSR原理示例深入解析

    React?Streaming?SSR原理示例深入解析

    這篇文章主要為大家介紹了React?Streaming?SSR原理示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React Native如何消除啟動(dòng)時(shí)白屏的方法

    React Native如何消除啟動(dòng)時(shí)白屏的方法

    本篇文章主要介紹了React Native如何消除啟動(dòng)時(shí)白屏的方法,詳細(xì)的介紹了解決的方法,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • react-router-dom6(對(duì)比?router5)快速入門指南

    react-router-dom6(對(duì)比?router5)快速入門指南

    這篇文章主要介紹了快速上手react-router-dom6(對(duì)比?router5),通過(guò)本文學(xué)習(xí)最新的react-router-dom?v6版本的路由知識(shí),并且會(huì)與v5老版本進(jìn)行一些對(duì)比,需要的朋友可以參考下
    2022-08-08
  • React利用插件和不用插件實(shí)現(xiàn)雙向綁定的方法詳解

    React利用插件和不用插件實(shí)現(xiàn)雙向綁定的方法詳解

    我們知道在 angular 中數(shù)據(jù)時(shí)雙向綁定的;而在 react 中,數(shù)據(jù)是向一個(gè)方向傳遞:從擁有者到子節(jié)點(diǎn)。也就是我們說(shuō)的單向數(shù)據(jù)綁定。那如何實(shí)現(xiàn)雙向綁定呢?下面這篇文章主要給大家介紹了關(guān)于React利用插件和不用插件實(shí)現(xiàn)雙向綁定的方法,需要的朋友可以參考下。
    2017-07-07
  • React實(shí)現(xiàn)數(shù)字滾動(dòng)組件numbers-scroll的示例詳解

    React實(shí)現(xiàn)數(shù)字滾動(dòng)組件numbers-scroll的示例詳解

    數(shù)字滾動(dòng)組件,也可以叫數(shù)字輪播組件,這個(gè)名字一聽(tīng)就是非常普通常見(jiàn)的組件。本文將利用React實(shí)現(xiàn)這一組件,感興趣的小伙伴可以了解一下
    2023-03-03
  • React從Class方式轉(zhuǎn)Hooks詳解

    React從Class方式轉(zhuǎn)Hooks詳解

    這篇文章主要介紹了React從Class方式轉(zhuǎn)Hooks詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • React中使用antd組件的方法

    React中使用antd組件的方法

    antd?是基于?Ant?Design?設(shè)計(jì)體系的?React?UI?組件庫(kù),主要用于研發(fā)企業(yè)級(jí)中后臺(tái)產(chǎn)品,這篇文章主要介紹了React中使用antd組件,需要的朋友可以參考下
    2022-09-09
  • React實(shí)現(xiàn)倒計(jì)時(shí)功能組件

    React實(shí)現(xiàn)倒計(jì)時(shí)功能組件

    這篇文章主要為大家詳細(xì)介紹了如何通過(guò)React實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)功能組件,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下
    2023-09-09
  • React前端框架實(shí)現(xiàn)原理的理解

    React前端框架實(shí)現(xiàn)原理的理解

    React是前端開(kāi)發(fā)每天都用的前端框架,自然要深入掌握它的原理。我用?React?也挺久了,這篇文章就來(lái)總結(jié)一下我對(duì)?react?原理的理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-07-07

最新評(píng)論