iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 15
1
Modern Web

I Want To Know React系列 第 15

I Want To Know React - 條件 render

React 條件 render

使用情境

在撰寫前端頁面時,常常需要根據不同的 props / state 資料顯示不同的 element。

舉例來說,使用者尚未登入的話要顯示登入按鈕;使用者已經登入的話則顯示登出按鈕。

在這章節中,我們就要來學習如何根據不同的條件 render 出對應的 element。

實作概念

條件 render 其實沒有任何特殊的語法,它只是 JavaScript 與 JSX 的應用而已。任何 if else expression / statement 的條件判斷方式都可以用來條件 render。

初探 Component & Props 篇所講,回傳 React element 的 function 就是一個合法的 component,而 component 內要執行哪些邏輯後才回傳 React element 則是開發者可以自由發揮的實作細節。

只要熟悉初探 JSXJSX 語法章節的內容,相信讀者一定能快速掌握條件 render 的精髓。

以下將說明 React 條件 render 的使用方式與常見技巧。這些方法都是可行的條件 Render 處理方式,讀者視可讀性與當下情境應用、延伸、選擇要使用的寫法。

技巧:if else 判斷要 render 的 component

第一種方法是透過 JavaScript 的 if else 選擇要 render 哪個 element。

承開頭所說的例子。假設我們有一個網頁:

  • 使用者尚未登入的話要顯示登入按鈕
  • 使用者已經登入的話則顯示登出按鈕

則可以這樣實作:

function LoginButton(props) {
	return <button>Login</button>;
}

function LogoutButton(props) {
	return <button>Logout</button>;
}

function LoginStatusPanel(props) {
	if (props.isLoggedIn) {
    return <LogoutButton/>;
  }
	return <LoginButton/>;
}

ReactDOM.render(
  <LoginStatusPanel isLoggedIn={false} />,
  document.getElementById('root')
);

可以看到這個範例只是根據不同的情境返回不同的 React element 而已。條件 render 內容如下:

  • 如果帶入 LoginStatusPanel 的 props 內容為 isLoggedIn === true 的話就回傳 <LogoutButton /> React element
  • 如果 LoginStatusPanel 的 props 內容為 isLoggedIn === false 的話則回傳 <LoginButton /> React element

技巧:用變數儲存要顯示的 element

第二種方式是把要 render 的 element 判斷好後存在變數中,並在 JSX Expression 中使用這個變數。

讓我們換個做法實作 LoginStatusPanel

function LoginButton(props) {
	return <button>Login</button>;
}

function LogoutButton(props) {
	return <button>Logout</button>;
}

function LoginStatusPanel(props) {
  const button = props.isLoggedIn ? <LogoutButton/> : <LoginButton/>
	return (
    <div>
      <h1>Login panel</h1>
      {button}
    </div>
  );
}

範例中,<LoginButton /><LogoutButton /> 會根據 props.isLoggedIn 的值判斷完後存到 button 變數中。

  • 如果 isLoggedInbutton 內容為 <LogoutButton />
  • 如果尚未 isLoggedInbutton 內容為 <LoginButton />

接著在 <div> 中就可以使用 expression:{button} 將判斷好的 element render 到畫面上。

技巧:用 ?: 決定 render 哪個 element

另外一個方法是直接在 JSX expression 中使用 JavaScript 的三元運算子?:來決定要 render 哪個 element。

三元運算子的用法如下:

condition ? exp1 : exp2
  • 如果 conditiontruthy 的話,回傳 exp1 的值
  • 如果 conditionfalsy 的話,則回傳 exp2 的值

就來用 ?: 改寫 LoginStatusPanel 範例吧:

function LoginButton(props) {
	return <button>Login</button>;
}

function LogoutButton(props) {
	return <button>Logout</button>;
}

function LoginStatusPanel(props) {
	return (
    <div>
      <h1>Login panel</h1>
      { props.isLoggedIn ? <LogoutButton/> : <LoginButton/> }
    </div>
  );
}

可以看到在 LoginStatusPanel 裡的 expression 中,我們用三元式 ?: 做為條件判斷選擇要 render 哪個 element。

  • 如果 isLoggedIn 加入 <LogoutButton /> 到 element 中
  • 如果尚未 isLoggedIn 則加入 <LoginButton /> 到 element 中

技巧:用 && 決定是否 render element

在一些需求下需要讓 component 符合條件就顯示特定 element,而不符合條件就不顯示。我們可以用 expression 中使用 JavaScript 的 && 運算子來達成這件事。

&& 運算子的用法如下:

exp1 && exp2
  • 如果 exp1truthy 的話,回傳 exp2 的值
  • 如果 exp1falsy 的話,則回傳 exp1 的值

讓我們來實際演練吧!

現在要實作一個電子郵件 component Mailbox

  • 如果有未讀信件的話,要顯示未讀提示
  • 沒有未讀信件的話就不顯示提示

則可以這樣實作:

function Mailbox(props) {
	const unreadMessages = props.unreadMessages;
	return (<div>
		<h1>Hello</h1>
		{unreadMessages.length > 0 &&
			<h2>You have {unreadMessages.length} unread messages</h2>
		}
  </div>);
}

範例中:

  • 如果 unreadMessages.length > 0(代表有未讀信件),會顯示 You have xxx unread messages
  • 如果 unreadMessages.length <= 0(沒未讀信件),則不會出現任何提示,因為 React 收到 false 就會忽略不 render

讓 component 不 render element

在一些特定的情形下,我們會希望 component 在條件判斷不成立的時候不要 render 任何 element。

在 React 中,只要讓 component 回傳以下其中一種值即可不 render 任何 element:

  • 回傳 null
  • 回傳 false

回傳 null 以不 render element

只要 component 回傳 null,React 就不會幫該 component render 任何內容。

舉例來說,如果要實作一個網路斷線提示條:NetworkDisconnectedBar,只在網路斷線的時候顯示。

我們可以用 if 判斷式的方式讓 component 在有網路的狀況下回傳 null 以不顯示任何東西:

function NetworkDisconnectedBar(props) {
	if (props.hasNetwork) return null;

	return <div>Network disconnected</div>;
}

ReactDOM.render(
  <NetworkDisconnectedBar hasNetwork />,
  document.getElementById('root')
);

讀者可以到 CodePen 查看結果。

可以看到範例中,因為 props.hasNetworktrue如果 attribute 沒有 value 的話,其 value 預設值為 true),使 NetworkDisconnectedBar 回傳 null,所以畫面不會為 NetworkDisconnectedBar 顯示任何 element。

回傳 false 以不 render element

同樣的,如果 component 回傳 false,React 也不會幫該 component render 任何內容。

延續剛剛的 NetworkDisconnectedBar 範例,我們可以應用上面段落學到的 && 運算子改寫剛剛的程式,讓 component 在有網路的狀況下回傳 false 以不顯示任何東西:

function NetworkDisconnectedBar(props) {
  return !props.hasNetwork && <div>Network disconnected</div>;
}

ReactDOM.render(
  <NetworkDisconnectedBar hasNetwork />,
  document.getElementById('root')
);

讀者可以到 CodePen 查看範例。

可以看到範例中,同樣的,因為 props.hasNetworktrue,使 NetworkDisconnectedBar 回傳 false,所以畫面不會為 NetworkDisconnectedBar 顯示任何 element。

常見錯誤:if else 寫在 JSX Expression 中

當筆者剛開始寫 JSX 條件 render 時常常會寫出錯誤的語法:把 if else 寫在 JSX expression 中。

if else 不能夠直接寫在 JSX expression 中,因為 if else 是 statement,而非 expression。

讓我們來做一次錯誤示範:

function LoginButton(props) {
	return <button>Login</button>;
}

function LogoutButton(props) {
	return <button>Logout</button>;
}

function LoginStatusPanel(props) {
	return (
    <div>
      <h1>Login panel</h1>
			{
				// Parsing error: Unexpected token if
				if (props.isLoggedIn) {
					return <LogoutButton/>;
				} else {
					return <LoginButton/>;
				}
			}
    </div>
  );
}

可以看到 expression 中一寫 if,就會跳出 Parsing error: Unexpected token if 的錯誤。

  • ?小提醒:如何判斷一段程式碼是否為 expression

    只要一段程式碼可以被當作參數傳進 function 即代表它是 expression,因為 function 的參數只接受 expression。

    如果想要更深入了解 JSX 語法與 expression 的讀者,可以閱讀 JSX 語法篇。

小結

React 中,條件 render 其實就只是 JavaScript 的 if else 與 JSX expression 的應用而已。在本章節中,介紹了一些技巧來做到 React 條件 render 的功能,包括:

  • if else 判斷要 render 的 component
  • 用變數儲存要顯示的 element
  • ?: 決定 render 哪個 element
  • && 決定是否 render element

這些技巧都是可行的 JSX 條件 Render 處理方式,讀者應視情況應用、延伸、選擇要使用的寫法。

另外我們也學習到了讓 component 不 render 的技巧:

  • 讓 component 回傳 null
  • 讓 component 回傳 false

最後,我們需要避免錯誤的把 if else 寫在 JSX expression 中,因為 if else 是 statement,非 expression,因此 JSX expression 不接受這種寫法。

參考資料


上一篇
I Want To Know React - 正確使用 Event handler
下一篇
I Want To Know React - Render list
系列文
I Want To Know React30

尚未有邦友留言

立即登入留言