筆記 - React Intro (1) Components
正式進入 React 的世界! 依照 React 官網 React Docs BETA 內容學習。
規則很多,筆記下來~避免搞混。
Your First Component
- What a component is
- What role components play in a React application
- How to write your first React component
React component 是可以添加 markup 語言(例如 HTML)的 Javascript function。
建立 component 三步驟
- Export the component:
export default
之後可以在其他地方import
後使用 - Define the function:
function Profile() { }
React component 命名一定大寫開頭 - Add markup: return
<img />
tag. JSX 語法,很像 HTML,可以讓我們在 JavaScript 嵌入 markup 語法(實際上還是 JavaScript)。 如果 return 多行,要用小括號包起來
使用 component
定義好的 component 可以 nest inside other component 中。
例如:
1 | function Profile() { |
<section>
tag 是小寫,所以 React 會知道這是 HTML tag<Profile />
是大寫開頭,所以 React 知道這是名為 Profile 的 component
Profile
元件裡面包含 HTML <img />
,所以最終瀏覽器會看到以下:
1 | <section> |
注意事項
Nesting and organizing components:
- component 是 js function,所以一個檔案可以包含複數個 components
- 在上面的範例中,
Profile
在Gallery
裡面被渲染,所以稱Gallery
為父元件 (parent component),Profile
為其子元件(child component)。 - component 中可以渲染其他 component,但不要在裡面寫定義,如同 js function,一個 function 定義一個行為就好。如果 parent 要傳參數給 child,後續會說明如何傳遞。
Importing and Exporting Components
- What a root component file is
- How to import and export a component
- When to use default and named imports and exports
- How to import and export multiple components from one file
- How to split components into multiple files
The root component file
從 npx create-react-app my-app
建立專案的話,root component file 為 src/App.js
,如果使用其他框架(ex. Next.js) 就會不同。
Exporting and importing a component
當專案規模變大,元件變多時,可以分離元件,使其模組化更容易重複使用。當想要把 component 分離到新的檔案時,有三個步驟:
- 建立一個新的
.js
file,把 component 放進去 - 將 function component export 出去 (default 或 named exports 皆可)
- 在要使用的檔案中 import component (根據之前是 default or named exports 寫法些許不同)
例如
1 | // 新建立 Gallery.js |
Default vs named exports 寫法注意事項
原則:一個檔案可以有多個 named exports 但是只能有一個 default export。
寫法:
Syntax | Export statement | Import statement |
---|---|---|
Default | export default function Button() {} |
import Button from './button.js' |
Named | export function Button() {} |
import { Button } from '.button.js' |
Note:
- import 檔案名稱
'./button.js'
有無寫副檔名皆可,但有寫比較符合 native JavaScript Module 規則。 default
import 後面可以改名字,例如import Banana from './button.js'
,但在 named imports 兩邊名字要相同。- 通常如果一個檔案中只有一個 function component 要 export, 就使用 default exports;如果有多個 components 或 values 要匯出,就使用 named exports。
- 不要使用匿名函式
export default () => {}
。
Exporting and importing multiple components from the same file
上方例子中,若想將 Profile
component 也匯出作為 App 的子元件,修改如下
1 | // Gallery.js |
Writing Markup with JSX
- Why React mixes markup with rendering logic
- How JSX is different from HTML
- How to display information with JSX
以前網頁分成 HTML 頁面內容, CSS 頁面外觀, JavaScript 邏輯三個檔案,隨著網頁的互動性需求提升,邏輯逐漸掌管了內容,可以說是 JavaScript 主導 HTML 內容,所以 React 將渲染邏輯與 markup 都放在同個地方:component 之中。
React component 是包含了 rendering logic and markup 的 JavaScript function。使用 JSX syntax extension 讓我們在 js 檔案中寫 markup。JSX 長得很像 HTML, 但稍微比較嚴格,且可以顯示動態資訊。
Converting HTML to JSX
假設目前有一些 HTML 想要塞到 component 中,直接貼在 return () 裡面是會 error 的,需要 follow JSX 的規則:
Return a single root element
React component function 中只能 return 一個元素,所以用 <div></div>
,若位置不適合放 div 時(例如tbody裡面)或其他原因不想出現在 HTML tree 時,則可使用空的 tag <> </>
(稱作Fragment)。
THINK: 為什麼 JSX 要把 tag 打成一包才能 return 呢?
JSX 雖然長得像 HTML,但他終究是會被轉成 JavaScript objects 的。一個 function 也不能 return 多個 objects,所以要用另一個 tag 或 Fragment 打包起來。
Close all the tags
JSX 嚴格規定 tag closing 的部分
- self-closing tags:
<img />
- wrapping tags:
<li> ... </li>
camelCase all most of the things!
JSX 會轉成 JavaScript 而 JSX attribute name 會成為 JavaScript object 的 key,注意 class
為 reserved word, 所以在 React 中寫 className
做代替。
aria-*
與 data-*
還是保持與 HTML 同樣用 dash 連接。
也可以試試 JSX Converter。
Javascript in JSX with Curly Braces
- How to pass strings with quotes
- How to reference a JavaScript variable inside JSX with curly braces
- How to call a JavaScript function inside JSX with curly braces
- How to use a JavaScript object inside JSX with curly braces
Passing strings with quotes
JSX 中,屬性值字串可以用單或雙引號(''
或 ""
),如果是變數就用單大括號 {}
包。
Using curly braces: A window into the JavaScript world
{}
除了變數,可以放任何的 JavaScript expression,下面例子為 function formatDate()
:
1 | const today = new Date(); |
使用 {}
注意事項:
- 當成文字使用在 JSX tag 內,tag 本身不行。例如
<h1> {name}'s todo </h1>
可以,<{tag}>
不行。 - 當成屬性放在
=
後面。例如scr={avatar}
。
Using double curly braces: CSS and other objects in JSX
JavaScript object 也可以放到 JSX 中,object 本身就用 {}
包著,例如 { name: "Hedy Lamarr", inventions: 5 }
,所以在使用的時候,就會寫兩個括號 person={{ name: "Hedy Lamarr", inventions: 5}}
。
如果有需要的話,JSX 中也可以使用 inline CSS styles,使用時也是將屬性用 object 傳入 style
中。
要注意的事為 inline style
properties 要用 camelCase 寫。HTML 中會寫 style="background-color: black"
, JSX 中寫 style="backgroundColor: black"
。
1 | export default function TodoList() { |
總之在 JSX 看到雙花括,不是 JavaScript object 就是 inline CSS style 屬性值。
綜合應用
JavaScript object person
包含 name
字串以及 theme
物件。用 {person.name}
傳入字串,用 {person.theme}
傳入 inline CSS style 屬性物件。
1 | const person = { |
Passing Props to a Component
- How to pass props to a component
- How to read props from a component
- How to specify default values for props
- How to pass some JSX to a component
- How props change over time
Props (應該)就是 properties 的縮寫
Familiar props
Props 是傳遞給 JSX tag 的資訊,例如可以傳 className
, src
, alt
, width
, height
給 <img>
tag,基本上與 HTML tag 相同。
在 React 中,可以傳入任何 properties 給 component。
Passing props to a component
Profile
component pass some props to its child component, Avatar
.
Pass props to the child component
此範例中,父元件 Profile
傳兩個 props: person
(an object), and size
(a number) 給子元件 Avatar
:
1 | export default function Profile() { |
傳入後,在 Avatar
component 中可以讀到 props 的值。
Read props inside the child component
在 function Avatar
後面列出要讀取的 props 名稱,用({
})
包起來,像是函數變數一樣。
1 | function Avatar({ person, size }) { |
props 是 React component function (only) argument,所以也可以寫成下面的方式,就不用列出所有 props 裡面的名字(destructuring):
1 | function Avatar(props) { |
Specify a default value for a prop
想給 prop 一個預設值,可以這樣寫:
1 | function Avatar({ person, size = 100 }) { |
如果 <Avatar person={...} />
渲染時沒有 size 的屬性值時,就會預設為 100,避免出現 size={undefined}
。
Forwarding props with the JSX spread syntax
當 component 要傳 所有 的 props 給其 child component 時,可以用 spread syntax 來減少重複的 code。
1 | function Profile({ person, size, isSepia, thickBorder }) { |
寫成
1 | function Profile(props) { |
Passing JSX as children
像是 HTML tags 會 nesting 一樣,有時也會將 components nesting
1 | <Card> |
When you nest content inside a JSX tag, the parent component will receive that content in a prop called children
. For example, the Card component below will receive a children prop set to <Avatar />
and render it in a wrapper div:
children
props 這個寫法在 visual wrappers (like panels, grids) 很常使用,可以想成 Card 裡面有個 children 的洞,可以放入 Avatar, text 等不同內容。
1 | // @App.js |
How props change over time
prop 值不能改變,但 component 可動態從 parent component 接收新的 prop object。
如果要想要 component 隨著使用者輸入而改變,會需要 set state(後續學習)。