React元素與組件的區(qū)別示例詳解
從問題出發(fā)
我被問過這樣一個問題:
想要實現(xiàn)一個 useTitle 方法,具體使用示例如下:
function Header() {
const [Title, changeTitle] = useTitle();
return (
<div onClick={() => changeTitle('new title')}>
<Title />
</div>
)
}
但在編寫 useTitle 代碼的時候卻出了問題:
function TitleComponent({title}) {
return <div>{title}</div>
}
function useTitle() {
const [title, changeTitle] = useState('default title');
useEffect(() => {
changeTitle(title)
}, [title])
const Element = React.createElement(TitleComponent, {title});
return [Element.type, changeTitle];
}
這段代碼直接報錯,連渲染都渲染不出來,如果是你,該如何修改這段代碼呢?
元素與組件
其實這就是一個很典型的元素與組件如何區(qū)分和使用的問題。
元素
我們先看 React 官方文檔中對 React 元素的介紹:
Babel 會把 JSX 轉(zhuǎn)譯成一個名為 React.createElement() 函數(shù)調(diào)用。以下兩種示例代碼完全等效:
const element = <h1 className="greeting">Hello, world!</h1>;
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
React.createElement() 會預(yù)先執(zhí)行一些檢查,以幫助你編寫無錯代碼,但實際上它創(chuàng)建了一個這樣的對象:
// 注意:這是簡化過的結(jié)構(gòu)
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
這些對象被稱為 “React 元素”。它們描述了你希望在屏幕上看到的內(nèi)容。
你看,React 元素其實就是指我們?nèi)粘>帉懙?JSX 代碼,它會被 Babel 轉(zhuǎn)義為一個函數(shù)調(diào)用,最終得到的結(jié)果是一個描述 DOM 結(jié)構(gòu)的對象,它的數(shù)據(jù)結(jié)構(gòu)本質(zhì)是一個 JS 對象。
在 JSX 中,我們是可以嵌入表達式的,就比如:
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
所以如果我們要使用一個 React 元素,那我們應(yīng)該使用嵌入表達式這種方式:
const name = <span>Josh Perez</span>;
const element = <h1>Hello, {name}</h1>;
組件
那組件呢?組件有兩種,函數(shù)組件和 class 組件:
// 函數(shù)組件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// class 組件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
那如何使用組件呢?
const element = <Welcome name="Sara" />;
對于組件,我們要使用類似于 HTML 標(biāo)簽的方式進行調(diào)用,Babel 會將其轉(zhuǎn)譯為一個函數(shù)調(diào)用
const element = React.createElement(Welcome, {
name: "Sara"
});
所以你看,組件的數(shù)據(jù)結(jié)構(gòu)本質(zhì)是一個函數(shù)或者類,當(dāng)你使用元素標(biāo)簽的方式進行調(diào)用時,函數(shù)或者類會被執(zhí)行,最終返回一個 React 元素。
問題如何解決
盡管這些內(nèi)容都來自于 React 官方文檔,但如果你能清晰的了解到 React 元素和組件的差別,你已經(jīng)可以解決開頭的問題了。至少有兩種方式可以解決,一種是返回 React 元素,一種是返回 React 組件
第一種我們返回 React 元素:
const root = ReactDOM.createRoot(document.getElementById('root'));
function Header() {
const [Title, changeTitle] = useTitle();
// 這里因為返回的是 React 元素,所以我們使用 {} 的方式嵌入表達式
return (
<div onClick={() => changeTitle('new title')}>
{Title}
</div>
)
}
function TitleComponent({title}) {
return <div>{title}</div>
}
function useTitle() {
const [title, changeTitle] = useState('default title');
useEffect(() => {
changeTitle(title)
}, [title])
// createElement 返回的是 React 元素
const Element = React.createElement(TitleComponent, {title});
return [Element, changeTitle];
}
root.render(<Header />);
第二種我們返回 React 組件:
const root = ReactDOM.createRoot(document.getElementById('root'));
function Header() {
const [Title, changeTitle] = useTitle();
// 因為返回的是 React 組件,所以我們使用元素標(biāo)簽的方式調(diào)用
return (
<div onClick={() => changeTitle('new title')}>
<Title />
</div>
)
}
function TitleComponent({title}) {
return <div>{title}</div>
}
function useTitle() {
const [title, changeTitle] = useState('default title');
useEffect(() => {
changeTitle(title)
}, [title])
// 這里我們構(gòu)建了一個函數(shù)組件
const returnComponent = () => {
return <TitleComponent title={title} />
}
// 這里我們直接將組件返回出去
return [returnComponent, changeTitle];
}
root.render(<Header />);
自定義內(nèi)容
有的時候我們需要給組件傳入一個自定義內(nèi)容。
舉個例子,我們實現(xiàn)了一個 Modal 組件,有確定按鈕,有取消按鈕,但 Modal 展示的內(nèi)容為了更加靈活,我們提供了一個 props 屬性,用戶可以自定義一個組件傳入其中,用戶提供什么,Modal 就展示什么,Modal 相當(dāng)于一個容器,那么,我們該怎么實現(xiàn)這個功能呢?
第一種實現(xiàn)方式
以下是第一種實現(xiàn)方式:
function Modal({content}) {
return (
<div>
{content}
<button>確定</button>
<button>取消</button>
</div>
)
}
function CustomContent({text}) {
return <div>{text}</div>
}
<Modal content={<CustomContent text="content" />} />
根據(jù)前面的知識,我們可以知道,content 屬性這里傳入的其實是一個 React 元素,所以 Modal 組件的內(nèi)部是用 {} 進行渲染。
第二種實現(xiàn)方式
但第一種方式,并不總能解決需求。有的時候,我們可能會用到組件內(nèi)部的值。
就比如一個倒計時組件 Timer,依然提供了一個屬性 content,用于自定義時間的展示樣式,時間由 Timer 組件內(nèi)部處理,展示樣式則完全由用戶自定義,在這種時候,我們就可以選擇傳入一個組件:
function Timer({content: Content}) {
const [time, changeTime] = useState('0');
useEffect(() => {
setTimeout(() => {
changeTime((new Date).toLocaleTimeString())
}, 1000)
}, [time])
return (
<div>
<Content time={time} />
</div>
)
}
function CustomContent({time}) {
return <div style={{border: '1px solid #ccc'}}>{time}</div>
}
<Timer content={CustomContent} />
在這個示例中,我們可以看到 content 屬性傳入的是一個 React 組件 CustomContent,而 CustomContent 組件會被傳入 time 屬性,我們正是基于這個約定進行的 CustomContent 組件的開發(fā)。
而 Timer 組件內(nèi)部,因為傳入的是組件,所以使用的是 <Content time={time}/>進行的渲染。
第三種實現(xiàn)方式
在面對第二種實現(xiàn)方式的需求時,除了上面這種實現(xiàn)方式,還有一種稱為 render props 的技巧,比第二種方式更常見一些,我們依然以 Timer 組件為例:
function Timer({renderContent}) {
const [time, changeTime] = useState('0');
useEffect(() => {
setTimeout(() => {
changeTime((new Date).toLocaleTimeString())
}, 1000)
}, [time])
// 這里直接調(diào)用傳入的 renderContent 函數(shù)
return (
<div>
{renderContent(time)}
</div>
)
}
function CustomContent({time}) {
return <div style={{border: '1px solid #ccc'}}>{time}</div>
}
root.render(<Timer renderContent={(time) => {
return <CustomContent time={time} />
}} />);
鑒于我們傳入的是一個函數(shù),我們把 content 屬性名改為了 renderContent,其實叫什么都可以。
renderContent 傳入了一個函數(shù),該函數(shù)接收 time 作為參數(shù),返回一個 React 元素,而在 Timer 內(nèi)部,我們直接執(zhí)行了 renderContent 函數(shù),并傳入內(nèi)部處理好的 time 參數(shù),由此實現(xiàn)了用戶使用組件內(nèi)部值自定義渲染內(nèi)容。
多說一句,除了放到屬性里,我們也可以放到 children 里,是一樣的:
function Timer({children}) {
// ...
return (
<div>
{children(time)}
</div>
)
}
<Timer>
{(time) => {
return <CustomContent time={time} />
}}
</Timer>
我們可以視情況選擇合適的傳入方法。
React 系列
以上就是React元素與組件的區(qū)別示例詳解的詳細(xì)內(nèi)容,更多關(guān)于React元素組件區(qū)別的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React-router4路由監(jiān)聽的實現(xiàn)
這篇文章主要介紹了React-router4路由監(jiān)聽的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
React獲取Java后臺文件流并下載Excel文件流程解析
這篇文章主要介紹了React獲取Java后臺文件流下載Excel文件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
react-navigation 如何判斷用戶是否登錄跳轉(zhuǎn)到登錄頁的方法
本篇文章主要介紹了react-navigation 如何判斷用戶是否登錄跳轉(zhuǎn)到登錄頁的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
詳解React項目的服務(wù)端渲染改造(koa2+webpack3.11)
本篇文章主要介紹了詳解React項目的服務(wù)端渲染改造(koa2+webpack3.11),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
解析React中useMemo與useCallback的區(qū)別
這篇文章主要介紹了React中useMemo與useCallback的區(qū)別,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08
React?useEffect不支持async?function示例分析
這篇文章主要為大家介紹了React?useEffect不支持async?function示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07

