ReactJS 教學系列影片筆記
前言
本篇是參考 ReactJS Tutorial 的重點摘錄學習筆記。內容著重於 React 16.8 以前沒有 Hooks 時,以 Class Component 為主的教學。
閱讀前具備知識:
- 基礎網頁前端 (HTML、CSS、JS) 知識
- 基礎 ReactJS 知識
5 關於 export, import 和命名
使用 export default 輸出的話,import 的時候可以自訂名稱。
使用 export 輸出的話,import 的時候要用一樣的名稱,並且用 {}
包住。例:import { Greet } from './component/test';
更多詳細內容請參考:JavaScript Module Cheatsheet
9 props.children
props 是 read-only 變數。
- props - Functional Component
- this.props - Class Component
可以將客製化的 html 內層,寫在 component 外,但是在component內用 props.children 可以抓到:
1 | // Person component |
1 | // 引用 Person component 時,客製化 html 內層 |
11 setState 有三種寫法
props 是 read-only 變數。
- useState Hook - Functional Component
- this.state - Class Component
1 | // 一、傳入一個物件當作參數 |
1 | // 二、setState 模擬異步行為 |
1 | // 三、批量更新 |
參考資料:
React 中 setState() 为什么是异步的
組件的 state 和 setState
13 Event Handling
將事件處理器 Event Hander 的變數放在 {}
中。
1 | <button onClick={這邊是要傳入一個 function/method 參數作為事件處理器}> |
不要直接在 {}
中呼叫函式,錯誤寫法:
1 | <button onClick={clickHandler()}> |
在 Functional Component
中,事件處理器不需要綁定 this,因為沒有 this。
14 Binding Event Handlers
在 Class Component
中,當 method 或事件處理器 (clickHandler) 中會需要用到 class 中的其他 property or method (例如:this.state and this.methed) 時,就需要將該 state 或 method 的 this 和 class 的 this 綁定,確保 this 表示的是 class 而不是 tag。
1 | // 一、將 bind 寫在 render() 事件的參數中 |
錯誤寫法
1 | // 以下這個 method 還沒和 class 綁在一起 |
1 | // 二、render() 事件的參數中使用箭頭函示,再呼叫 clickHandler |
1 | // 三、在建構子中綁定,render() 事件中參數簡化 |
1 | // 四、使用箭頭函示,將 clickHandler 設為 class 的 property |
15 Methods as props
child component 如何和 parent component 溝通,或將值回傳至 parent component:
1 | // parent component |
1 | // child component |
16 Conditional Rendering 四種條件渲染
JSX 是語法糖,只允許函式呼叫和物件格式,不允許 if/else 寫在裡面,只能將 if/else 寫在 return (JSX) 外面。
但是三元運算符 (ternary conditional operator) 可以寫在 JSX 裡面。
1 | // 短路求值 short circuit evaluation |
19 Index as Key Anti-pattern 可以使用 index 當 key 的條件
- 陣列裡的 items 沒有 unique ID。
- 陣列是靜態的,不會改變。
- 陣列不會被重新排列或過濾 (filter)。
20 Styling and CSS Basics
- CSS stylesheets (className)
- Inline styling (記得不能用 font-size 要用 fontSize)
- CSS Modules (react-script 2.0 以上版本)
22 Component Lifecycle Methods 三階段 + 錯誤處理
React Lifecycle Methods Diagram
- Mounting
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
- Updating
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
- Unmounting
- componentWillUnmount()
- Error Handling
- static getDerivedStateFromError()
- componentDidCatch()
23 Component Mounting Lifecycle Methods
24 Component Updating Lifecycle Methods
25 Fragments (without adding extra nodes to the DOM)
每次要 return 時,只能回傳一個標籤組,但是用 <div></div>
又會多一組額外的標籤,可以改用 React.Fragment
,好處是用 array.map render 時可以加 key。
React.Fragment
可以用空標籤 (<></>
) 代替,缺點是不能加 key。
1 | return( |
26 Pure Components
PureComponent 是一種 Component,繼承 React.PureComponent,如果 props 和 states 都沒有改變,預防不必要的 re-render,內部已經幫我們寫好 shouldComponentUpdate,大概長得如下:
1 | shouldComponentUpdate(prevProps, prevState){ |
主要是進行淺比較 Shallow Comparison (SC):
- Primitive types
- a 和 b 有一樣的值和型態,ex:
1
2
3let a = "Jenifer";
let b = "Jenifer";
// return true - Complex types 物件、陣列
- a 和 b 有一樣的 reference,ex:
1
2
3
4
5let a = [1, 2, 3];
let b = [1, 2, 3];
let c = a;
// a (SC) b return false
// a (SC) c return true
因為是比較 reference,所以當陣列用 push 新增一個 item 時,沒有改變 reference,PureComponent 會感覺不到,不會重新渲染,這不是我要的。使用 PureComponent 時,記得回傳一個新物件或陣列。
確保 PureComponent 的子層也都是 PureComponent,才不會有非預期的行為。
將 render props 與 React.PureComponent 一起用時
因為 每一次的 render prop 都是新的值,淺比較時永遠得到 false -> 會重新渲染
解決方法:在上一層裡面定義靜態 render prop
27 memo
react and react-dom 16.6.0 以上的版本,才有此功能。
React.memo is a higher order component (HOC). 它是 function component 版本的 PureComponent,如果 props 和 states 都沒有改變,預防不必要的 re-render。
因為是 HOC 所以用法跟 PureComponent 不一樣,如下:
1 | import React from "react"; |
28 Refs 直接 access to DOM
將 Refs 和某個 element 綁在一起,即可使用 Refs 直接取得 DOM,呼叫其 value 和 method。
1 | // 一、在建構子中使用 React.createRef() |
1 | // 二、React.createRef() 寫在建構子外 |
this.inputRef.current
可以直接取到 DOM 元素,若是使用 console.log(this.inputRef.current)
會看到如下左圖:
類似 console.dir(element);
如上右圖。
關於 Refs 舊版的寫法,用到 callback:
1 | // 一、在建構子中使用 React.createRef() |
29 Refs with Class Components 從父層呼叫子層的 method (一)
Refs 只能和 element 和 class component 綁在一起。Refs 不能和 functional component 綁在一起。
this.inputRef.current
可以直接取到 DOM 元素,可以利用這個特性,從父層呼叫子層的 method。
1 | // Parents component |
1 | // Child component |
30 Forwarding Refs 從父層呼叫子層的 method (二)
使用 forwardRef,將父層中的 Refs 直接指向「原生的 component」,另類的從父層呼叫子層的 method。
只有在子層中使用 forwardRef 的寫法,才能呼叫 Refs。
1 | // Parents component |
1 | // Child component |
31 Portals
Portals 提供了一種很好的將子節點渲染到父组件以外的 DOM 節點的方式。
如果今天想要製造一個額外的 popup 或提示視窗,UI 想要不被父層的 css 限制,就不能將該 component 放進
1 | <div id="root"> |
1 | import React from "react"; |
儘管 portal 可以被放置在 DOM 樹的任何地方,但在其他方面其行為和普通的 React 子節點行為一致。如上下文特性依然能夠如之前一樣正確地工作,無論其子節點是否是 portal。只要考慮 portal 存在於 React 樹中的位置,而不用考慮其在 DOM 樹中的位置。
如果今天有一個 portal 組件被放在DOM 樹 #modal-root
中,而 React 樹中被放在 #app-root
底下。
在 #app-root
裡的父層能夠捕獲到 portal 組件冒泡上来的事件。
1 | <body> |
32 Error Boundary
Error Boundary 是一種 component,可以捕捉被 ErrorBoundary component 包圍住的所有子層在渲染期間、生命週期方法內和建構子內發生的錯誤。並且回傳一個錯誤訊息的 UI。
這個可以用在電商的網站,如果僅一個商品或組建出錯,我們不會希望全部的其他 999 件商品,整個網站都無法顯示,使用 Error Boundary 可以產生一個錯誤訊息的 UI。
創造一個 class 的 ErrorBoundary 組件的方法就是,在 class 中呼叫 static getDerivedStateFromError(error){}
或 componentDidCatch(error, info){}
其中一個或兩者。
1 | <ErrorBoundary> |
1 | class ErrorBoundary extends React.Component { |
ErrorBoundary 放置的位置可以是只包一個組件,或包住全部的組件。但是只要被包住的範圍出現錯誤,被包住的組件就不會被渲染出來,會渲染出 fallback UI。
33 Higher Order Components (Part 1)
使用 HOC 的原因:當有兩個以上的元件,生命週期做的事情相同時,需要寫很多次一樣或類似的程式碼,程式碼不簡約而且有很多重複的部分,這時後就可以利用 HOC 的特性。
最初有人這樣想,將 state 提升到父層,並且將處理事件的 handler 當成 props 傳遞給子層。
但是萬一如果父層和子層差距很遠怎麼辦?需要用到該 state 和 handler 的元件可能散布在 React 樹中各處,因此這個想法某些時候不適用。
34 Higher Order Components (Part 2)
HOC 是一個 function,將 component 作為 argument 傳入,並且回傳一個新的 component,會長得有點像這樣:
const newComponent = HOC(originalComponent)
經過 A HOC 的 component 會有 A 的特性和功能; 經過 B HOC 的 component 會有 B 的特性和功能。
可以寫成這樣:
1 | import React from "react"; |
也可以寫成這樣:
1 | import React from "react"; |
現在來舉個真實的例子,並套用命名慣例:
1 | // withCounter.js |
1 | // ClickBtn.js |
1 | // HoverTitle.js |
35 Higher Order Components (Part 3)
一、記得傳入其餘參數
如果我直接在 App.js 中,想將傳遞的參數寫入 (如下),會出錯!!!!!! this.props.name
會被傳入 withCounter function 中,沒有傳到 ClickBtn 這個 class component 中。
1 | render() { |
既然 this.props.name
有被傳入 withCounter function 中,修正方法就是在 withCounter (HOC) 中對 WrappedComponent
增加 {...this.props}
,將其餘的 props
也傳入該元件。
1 | // withCounter.js |
二、HOC function 可以傳入其他參數 parameters
1 | // withCounter.js |
1 | // HoverTitle.js |
很多 library 都有用到 HOC,例:
React Redux 中的 connect。
react-router 中的 withRouter。
Material-UI 中的 withStyles。