新聞中心
條件渲染方式
一、if 語句
先從 React 最基本的條件類型來看。如果有數(shù)據(jù)就顯示組件,如果沒有數(shù)據(jù)就不顯示任何內(nèi)容。posts 為需要渲染的列表:

export default function App() {
const { posts } = usePosts();
if (!posts) return null;
return (
);
}這種形式會生效的原因就是我們會提前返回,如果滿足條件(posts 值不存在),就通過return null 在組件中不顯示任何內(nèi)容。
如果有多個要檢查的條件時,也可以使用 if 語句。例如,在顯示數(shù)據(jù)之前檢查加載和錯誤狀態(tài):
export default function App() {
const { isLoading, isError, posts } = usePosts();
if (isLoading) return Loading...;
if (isError) return Error!;
return (
);
}這里我們可以多次使用 if 語句,不需要再使用 else 或者 if-eles 語句,這樣就減少了需要編寫的代碼,并且可讀性更強。
二、三元運算符
當我們想提前退出或者什么都不顯示時,if 語句會很有用。但是,如果我們不想寫一個與返回的 JSX 分開的條件,而是直接在其中寫呢?那就可以使用三元表達式來編寫條件。
在 React 中,我們必須在 JSX 中包含表達式,而不是語句。這就是為什么我們在 JSX 中只能使用三元表達式,而不是 if 語句來編寫條件。
例如,在移動設(shè)備的屏幕上顯示一個組件,而在更大的屏幕上顯示另一個組件,就可以使用三元表達式來實現(xiàn):
export default function App() {
const isMobile = useWindowSize()
return (
{isMobile ? : }
)
}其實,不必將這些三元表達式包含在 JSX 中,可以將其結(jié)果分配給一個變量,然后在需要的地方使用即可:
export default function App() {
const isMobile = useWindowSize();
const ChatComponent = isMobile ? : ;
return (
{ChatComponent}
)
}三、&&運算符
在許多情況下,我們可能想要使用三元表達式,但是如果不滿足條件,就不顯示任何內(nèi)容。那代碼會是這樣的:
condition ?: null.
可以使用 && 運算符來簡化:
export default function App() {
const { posts, hasFinished } = usePosts()
return (
<>
{hasFinished && (
已經(jīng)到底啦!
)}
>
)
}如果條件為真,則邏輯 && 運算符之后的表達式將是輸出。如果條件為假,React 會忽略并跳過表達式.
四、switch
過多的 if 語句會導致組件變得混亂,可以將多個條件提取到包含 switch 語句的單獨的組件中(根據(jù)組件邏輯的復雜程度來選擇是否提取到單獨的組件)。下面來看一個簡單的菜單切換組件:
export default function Menu() {
const [menu, setMenu] = React.useState(1);
const toggleMenu = () => {
setMenu((m) => {
if (m === 3) return 1;
return m + 1;
});
}
return (
<>
>
);
}
function MenuItem({ menu }) {
switch (menu) {
case 1:
return ;
case 2:
return ;
case 3:
return ;
default:
return null;
}
}由于使用帶有 switch 語句的 MenuItem 組件父菜單組件不會被條件邏輯弄亂,可以很容易地看到給定 menu 狀態(tài)將顯示哪個組件。需要注意,必須為 switch case 運算符使用默認值,因為在 React 中,組件始終需要返回一個元素或 null。
五、枚舉
在 JavaScript 中,當對象用作鍵值對的映射時,它可以用作枚舉:
const ENUMOBJECT = {
a: '1',
b: '2',
c: '3',
};假如要創(chuàng)建三個不同的組件 Foo、Bar 和 Default,并根據(jù)某種狀態(tài)顯示這些組件:
const Foo = () => {
return ;
};
const Bar = () => {
return ;
};
const Default = () => {
return ;
};創(chuàng)建可用作枚舉的對象:
const ENUM_STATES = {
foo: ,
bar: ,
default:
};渲染這個枚舉對象的函數(shù):
function EnumState({ state }) {
return {ENUM_STATES[state]};
}上面的 state 屬性可以從對象中檢索值。可以看到,與 switch case 運算符相比,它更具可讀性。
六、JSX 庫
JSX Control Statements 庫擴展了 JSX 的功能,從而可以直接使用 JSX 實現(xiàn)條件渲染。它是一個 Babel 插件,可以在轉(zhuǎn)譯過程中將類似組件的控制語句轉(zhuǎn)換為對應的 JavaScript。
安裝babel-plugin-jsx-control-statements包并修改 Babel 配置后,可以像這樣重寫應用程序:
export default function App(props) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
//...
return (
;
;
);
}當然,不建議這樣來編寫條件語句,這樣會導致代碼的可讀性變差,并且 JSX 允許使用強大的 JavaScript 功能來自己處理條件渲染,無需添加模板組件即可啟用它。
七、高階組件
高階組件 (HOC)與 React 中的條件渲染完美匹配。HOC 可以幫助處理多個用例,但一個用例可能是使用條件渲染來改變組件的外觀。讓我們看看顯示元素或組件的 HOC:
function withLoadingIndicator(Component) {
return function EnhancedComponent({ isLoading, ...props }) {
if (!isLoading) {
return ;
}
return (
Loading
);
};
}
const ListWithLoadingIndicator = withLoadingIndicator(List);
function App({ list, isLoading }) {
return (
);
}在這個例子中,List 組件可以專注于呈現(xiàn)列表。而不必再加載狀態(tài)。HOC 隱藏了實際組件中的所有干擾。最終,可以添加多個高階組件來隱藏多個條件渲染邊緣情況。
注意事項
一、小心 0
來看一個常見的渲染示例,當數(shù)組中存在元素時才渲染內(nèi)容:
{gallery.length && } 預想的結(jié)果是,數(shù)組存在元素時渲染內(nèi)容,不存在元素時什么都不渲染。但是,頁面上得到了 “0”。這是因為在使用與運算符時,一個假的左側(cè)值(如 0)會立即返回。在JavaScript中,布爾運算法不會將其結(jié)果轉(zhuǎn)化為布爾值。所以,React 將得到的值放入DOM中,與 false 不同的是,0 是一個有效的 React 節(jié)點,所以最終會渲染成0。
那該如何避免這個問題呢?可以顯式的將條件轉(zhuǎn)換為布爾值,當表達式結(jié)果為false時,就不會在頁面中渲染了:
gallery.length > 0 && jsx
!!gallery.length && jsx
Boolean(gallery.length) && jsx
或者使用三元表達式來實現(xiàn):
{gallery.length ? : null}二、優(yōu)先級
與運算符(&&)比或運算符(||)具有更高的優(yōu)先級。所以,要格外小心使用包含與運算符的 JSX 條件:
user.anonymous || user.restricted &&
這樣寫就相當于:
user.anonymous || (user.restricted && )
這樣,與運算符左側(cè)為真時就會直接返回,而不會繼續(xù)執(zhí)行后面的代碼。所以,多數(shù)情況下,看到或運算符時,就將其使用括號括起來,避免因為優(yōu)先級問題而渲染出錯:
{(user.anonymous || user.restricted) && }三、 嵌套三元表達式
三元表達式適合在兩個JSX之間進行切換,一旦超過兩個項目,代碼就會變得糟糕:
{
isEmoji
?
: isCoupon
?
: isLoaded &&
}有時使用 && 來實現(xiàn)會更好,不過一些條件判斷會重復:
{isEmoji && }
{isCoupon && }
{!isEmoji && !isCoupon && isLoaded && }當然,這種情況下,使用 if 語句可能是更好的選擇:
const getButton = () => {
if (isEmoji) return ;
if (isCoupon) return ;
return isLoaded ? : null;
};四、避免 JSX 作為條件
通過 props 傳遞的 React 元素能不能作為判斷條件呢?來看一個簡單的例子:
const Wrap = (props) => {
if (!props.children) return null;
return {props.children}
};我們希望 Wrap 在沒有包含內(nèi)容時呈現(xiàn) null,但 React 不是這樣工作的:
- props.children 可以是一個空數(shù)組,例如
{[].map(e => )} 。 - children.length 也失敗了:children 也可以是單個元素,而不是數(shù)組,例如:(
)。 - React.Children.count(props.children)支持單個子項和多個子項,但會認為
{false && 'hi'}{false && 'there'} 包含 2 個項,而實際上有沒有任何子項。 - React.Children.toArray(props.children) 移除無效節(jié)點,例如 false。然而,對于一個空片段,仍然是正確的:
<>> 。 - 如果將條件渲染移動到組件內(nèi):
與 Div = (p) => p.hide ?null : ,在 Wrap 渲染時永遠無法知道它是否為空,因為 react 只會在父級之后渲染子級 div,而有狀態(tài)的子級可以獨立于其父級重新渲染。
因此,不要將JSX作為判斷條件,避免出現(xiàn)一些難以預料的問題。
五、重新掛載還是更新?
用三元表達式編寫的 JSX 感覺就像是完全獨立的代碼:
{hasItem ? : }當 hasItem 改變時會發(fā)生什么?我的猜測是?? 。
注意:如果三元表達式包含的是不同的組件,如 {hasItem ?
: },hasItem改變時,React 會重新掛載,因為 Item1 無法更新為 Item2。
上述情況會導致一些意外的行為:
{
mode === 'name'
?
:
}這里,如果在 name 的 input 中輸入了一些內(nèi)容,然后切換模式(mode),在 name 中輸入內(nèi)容的就會泄漏到 phone 的 input 中,這可能會對依賴于先前狀態(tài)的復雜更新機制造成更大的破壞。
這里的一種解決方法是使用 key。通常,我們用它來渲染列表,但它實際上是 React 的元素標識提示——具有相同 key 的元素是相同的邏輯元素:
{
mode === 'name'
?
:
}另一種方法是用兩個單獨的 && 塊來替換三元表達式。當 key 不存在時,React 會回退到子數(shù)組中項目的索引,因此將不同的元素放在不同的位置與顯式定義 key 的效果是一樣的:
{mode === 'name' && }
{mode !== 'name' && }參考:
https://blog.thoughtspile.tech/2022/01/17/jsx-conditionals/。
https://ordinarycoders.com/blog/article/react-conditional-rendering。
網(wǎng)站標題:在React中實現(xiàn)條件渲染的七種方法
本文來源:http://m.5511xx.com/article/cddpcis.html


咨詢
建站咨詢
