本篇文章給大家整理分享 10 個編寫更簡潔 React 代碼的實用小技巧,希望對大家有所幫助!
1. JSX 簡寫
如何將 true 值傳遞給給定的 props?
在下面的示例中,使用 prop showTitle
在導(dǎo)航欄組件中顯示應(yīng)用的標(biāo)題:
export default function App() { return ( <main> <Navbar showTitle={true} /> </main> ); } function Navbar({ showTitle }) { return ( <div> {showTitle && <h1>標(biāo)題</h1>} </div> ) }
這里將 showTitle
顯式設(shè)置為布爾值 true
,其實這是沒必要的,因為組件上提供的任何 prop
都具有默認(rèn)值 true
。因此只需要在調(diào)用組件時傳遞一個 showTitle
即可:
export default function App() { return ( <main> <Navbar showTitle /> </main> ); } function Navbar({ showTitle }) { return ( <div> {showTitle && <h1>標(biāo)題</h1>} </div> ) }
另外,當(dāng)需要傳遞一個字符串作為 props
時,無需使用花括號 {}
包裹,可以通過雙引號包裹字符串內(nèi)容并傳遞即可:
export default function App() { return ( <main> <Navbar title="標(biāo)題" /> </main> ); } function Navbar({ title }) { return ( <div> <h1>{title}</h1> </div> ) }
2. 將不相關(guān)代碼移動到單獨的組件中
編寫更簡潔的 React 代碼的最簡單和最重要的方法就是善于將代碼抽象為單獨的 React 組件。
下面來看一個例子,應(yīng)用中最上面會有一個導(dǎo)航欄,并遍歷 posts
中的數(shù)據(jù)將文章標(biāo)題渲染出來:
export default function App() { const posts = [ { id: 1, title: "標(biāo)題1" }, { id: 2, title: "標(biāo)題2" } ]; return ( <main> <Navbar title="大標(biāo)題" /> <ul> {posts.map(post => ( <li key={post.id}> {post.title} </li> ))} </ul> </main> ); } function Navbar({ title }) { return ( <div> <h1>{title}</h1> </div> ); }
那我們怎樣才能讓這段代碼更加清潔呢?我們可以抽象循環(huán)中的代碼(文章標(biāo)題),將它們抽離到一個單獨的組件中,稱之為 FeaturedPosts
。抽離后的代碼如下:
export default function App() { return ( <main> <Navbar title="大標(biāo)題" /> <FeaturedPosts /> </main> ); } function Navbar({ title }) { return ( <div> <h1>{title}</h1> </div> ); } function FeaturedPosts() { const posts = [ { id: 1, title: "標(biāo)題1" }, { id: 2, title: "標(biāo)題2" } ]; return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
如你所見,在 App 組件中,通過其中的組件名稱:Navbar
和 FeaturedPosts
,就可以快速地看到應(yīng)用的作用。
3. 為每個組件創(chuàng)建單獨的文件
在上面的例子中,我們將三個組件在一個文件中實現(xiàn)。如果組件邏輯較少,這些寫還沒啥問題,但是如果組件邏輯較為復(fù)雜,那這樣寫代碼的可讀性就很差了。為了使應(yīng)用文件更具可讀性,可以將每個組件放入一個單獨的文件中。
這可以幫助我們在應(yīng)用中分離關(guān)注點。 這意味著每個文件只負(fù)責(zé)一個組件,如果想在應(yīng)用中重用它,就不會混淆組件的來源:
// src/App.js import Navbar from './components/Navbar.js'; import FeaturedPosts from './components/FeaturedPosts.js'; export default function App() { return ( <main> <Navbar title="大標(biāo)題" /> <FeaturedPosts /> </main> ); }
// src/components/Navbar.js export default function Navbar({ title }) { return ( <div> <h1>{title}</h1> </div> ); }
// src/components/FeaturedPosts.js export default function FeaturedPosts() { const posts = [ { id: 1, title: "標(biāo)題1" }, { id: 2, title: "標(biāo)題2" } ]; return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
此外,通過將每個單獨的組件包含在其自己的文件中,可以避免一個文件變得過于臃腫。
4. 將共享函數(shù)移動到 React hook 中
在 FeaturedPosts 組件,假設(shè)想要從 API 獲取文章數(shù)據(jù),而不是使用假數(shù)據(jù)。可以使用 fetch API 來實現(xiàn):
import React from 'react'; export default function FeaturedPosts() { const [posts, setPosts] = React.useState([]); React.useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then(res => res.json()) .then(data => setPosts(data)); }, []); return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
但是,如果想在多個組件執(zhí)行這個數(shù)據(jù)請求怎么辦?
假設(shè)除了 FeaturedPosts 組件之外,還又一個名為 Posts 的組件,其中包含相同的數(shù)據(jù)。 我們必須復(fù)制用于獲取數(shù)據(jù)的邏輯并將其粘貼到該組件中。為了避免重復(fù)編寫代碼,可以定義一個新的 React hook,可以稱之為 useFetchPosts:
import React from 'react'; export default function useFetchPosts() { const [posts, setPosts] = React.useState([]); React.useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then(res => res.json()) .then(data => setPosts(data)); }, []); return posts; }
這樣就可以在任何組件中重用它,包括 FeaturedPosts 組件:
import useFetchPosts from '../hooks/useFetchPosts.js'; export default function FeaturedPosts() { const posts = useFetchPosts() return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
5. 從 JSX 中刪除JS
另一種簡化組件的方式就是從 JSX 中刪除盡可能多的 JavaScript。來看下面的例子:
import useFetchPosts from '../hooks/useFetchPosts.js'; export default function FeaturedPosts() { const posts = useFetchPosts() return ( <ul> {posts.map((post) => ( <li onClick={event => { console.log(event.target, 'clicked!'); }} key={post.id}>{post.title}</li> ))} </ul> ); }
這里我們嘗試處理文章的點擊事件,可以看到我們的 JSX 變得更加難以閱讀。 鑒于函數(shù)是作為內(nèi)聯(lián)函數(shù)包含的,它掩蓋了這個組件的用途,以及它的相關(guān)函數(shù)。
那該如何來解決這個問題?可以將包含 onClick 的內(nèi)聯(lián)函數(shù)提取到一個單獨的處理函數(shù)中,給它一個名稱 handlePostClick。這樣 JSX 的可讀性就變高了:
import useFetchPosts from '../hooks/useFetchPosts.js'; export default function FeaturedPosts() { const posts = useFetchPosts() function handlePostClick(event) { console.log(event.target, 'clicked!'); } return ( <ul> {posts.map((post) => ( <li onClick={handlePostClick} key={post.id}>{post.title}</li> ))} </ul> ); }
6. 格式化內(nèi)聯(lián)樣式
在 JSX 中編寫過多的內(nèi)聯(lián)樣式就會讓代碼更難閱讀并且變得臃腫:
export default function App() { return ( <main style={{ textAlign: 'center' }}> <Navbar title="大標(biāo)題" /> </main> ); } function Navbar({ title }) { return ( <div style={{ marginTop: '20px' }}> <h1 style={{ fontWeight: 'bold' }}>{title}</h1> </div> ) }
我們要盡可能地將內(nèi)聯(lián)樣式移動到 CSS 樣式表中。或者將它們組織成對象:
export default function App() { const styles = { main: { textAlign: "center" } }; return ( <main style={styles.main}> <Navbar title="大標(biāo)題" /> </main> ); } function Navbar({ title }) { const styles = { div: { marginTop: "20px" }, h1: { fontWeight: "bold" } }; return ( <div style={styles.div}> <h1 style={styles.h1}>{title}</h1> </div> ); }
一般情況下,最好將這些樣式寫在CSS樣式表中,如果樣式需要動態(tài)生成,可以將其定義在一個對象中。
7. 使用可選鏈運算符
在 JavaScript 中,我們需要首先確保對象存在,然后才能訪問它的屬性。如果對象的值為undefined 或者 null,則會導(dǎo)致類型錯誤。
下面來看一個例子,用戶可以在其中編輯他們發(fā)布的帖子。只有當(dāng)isPostAuthor 為 true 時,也就是經(jīng)過身份驗證的用戶的 id 與帖子作者的 id 相同時,才會顯示該EditButton組件。
export default function EditButton({ post }) { const user = useAuthUser(); const isPostAuthor = post.author.userId !== user && user.userId; return isPostAuthor ? <EditButton /> : null; }
這段代碼的問題是 user 可能是 undefined. 這就是為什么我們必須在嘗試獲取 userId 屬性之前使用 && 運算符來確保 user 是一個對象。如果我要訪問一個對象中的另一個對象,就不得不再包含一個 && 條件。 這會導(dǎo)致代碼變得復(fù)雜、難以理解。
JavaScript 可選鏈運算符(?.)允許我們在訪問屬性之前檢查對象是否存在。用它來簡化上面的代碼:
export default function EditButton({ post }) { const user = useAuthUser(); const isPostAuthor = post.author.userId !== user?.userId; return isPostAuthor ? <EditButton /> : null; }
這樣將防止任何類型錯誤,并允許我們編寫更清晰的條件邏輯。
8. 帶括號的隱式返回
在 React 應(yīng)用中可以使用 function 關(guān)鍵字的函數(shù)聲明語法編寫組件,也可以使用設(shè)置為變量的箭頭函數(shù)。使用 function 關(guān)鍵字的組件必須在返回任何 JSX 之前使用 return 關(guān)鍵字。
export default function App() { return ( <Layout> <Routes /> </Layout> ); }
通過將返回的代碼包裹在一組括號中,可以通過隱式返回(不使用 return 關(guān)鍵字)從函數(shù)返回多行 JavaScript 代碼。
對于使用箭頭函數(shù)的組件,不需要包含 return 關(guān)鍵字,可以只返回帶有一組括號的 JSX。
const App = () => ( <Layout> <Routes /> </Layout> ); export default App;
此外,當(dāng)使用 .map()
迭代元素列表時,還可以跳過 return
關(guān)鍵字并僅在內(nèi)部函數(shù)的主體中使用一組括號返回 JSX。
function PostList() { const posts = usePostData(); return posts.map(post => ( <PostListItem key={post.id} post={post} /> )) }
9. 使用空值合并運算符
在 JavaScript 中,如果某個值是假值(如 null、undefined、0、''、NaN),可以使用 || 條件來提供一個備用值。
例如,在產(chǎn)品頁面組件需要顯示給定產(chǎn)品的價格,可以使用 || 來有條件地顯示價格或顯示文本“產(chǎn)品不可用”。
export default function ProductPage({ product }) { return ( <> <ProductDetails /> <span> {product.price || "產(chǎn)品不可用"} </span> </> ); }
現(xiàn)有的代碼存在一個問題,如果商品的價格為0,也不會顯示產(chǎn)品的價格而顯示"產(chǎn)品不可用"。如果左側(cè)為null
或者undefined
,而不是其他假值,就需要一個更精確的運算符來僅返回表達式的右側(cè)。
這時就可以使用空值合并運算符,當(dāng)左側(cè)操作數(shù)為null或者 undefined 時,將返回右側(cè)操作數(shù)。 否則它將返回其左側(cè)操作數(shù):
null ?? 'callback'; // "callback" 0 ?? 42; // 0
可以使用空值合并運算符來修復(fù)上面代碼中的問題:
export default function ProductPage({ product }) { return ( <> <ProductDetails /> <span>{product.price ?? "產(chǎn)品不可用"} </> ); }
10. 使用三元表達式
在 React 組件中編寫條件時,三元表達式是必不可少的,經(jīng)常用于顯示或隱藏組件和元素。
當(dāng)然,我們用可以使用三元表達式和模板字符串來給 React 元素動態(tài)添加或刪除類名。
export default function App() { const { isDarkMode } = useDarkMode(); return ( <main className={`body ${isDarkMode ? "body-dark" : "body-light"}`}> <Routes /> </main> ); }
這種條件邏輯也可以應(yīng)用于任何 props:
export default function App() { const { isMobile } = useDeviceDetect(); return ( <Layout height={isMobile ? '100vh' : '80vh'}> <Routes /> </Layout> ); }