# 從 Hooks 開始,讓你的網頁 React 起來系列的閱讀筆記(1-6) ###### tags: `閱讀筆記` `前端筆記` `React` ## 完成章節 - 1 - 6(2021/11/22) ## React 支援 JSX ### JSX 是什麼呢? JSX 是 JavaScript XML,雖然看起來很像 HTML 但是它並不全然是 HTML。 JSX 可以很方便地創造 React Element。 [*ref.:React JSX*](https://www.w3schools.com/react/react_jsx.asp) ```javascript= // 使用 JSX const myelement = <h1>I Love JSX!</h1>; ReactDOM.render(myelement, document.getElementById('root')); // 不使用 JSX const myelement = React.createElement('h1', {}, 'I do not use JSX!'); ReactDOM.render(myelement, document.getElementById('root')); ``` ### 為什麼在寫 React 的時候推薦使用 JSX? 因為在 React 的世界中所有的東西都是一個元件(Component)。每一個 Component 就是代表一個頁面的區塊。 ![](https://i.imgur.com/6B0SQwh.png) [*ref.: Full React Tutorial #3 - Components & Templates*](https://www.youtube.com/watch?v=9D1x7-2FmTA&list=PL4cUxeGkcC9gZD-Tvwfod2gaISzfRiP9d&index=3) 如上圖顯示,一個 Component 就是一個區塊(e.g. Navbar, Blog Text and Sidebar) 使用 JSX 可以幫助我們建立含 JavaScript 邏輯的 Component。(這點就像是之前上 Udemy 教的 EJS,就是可以直接在要渲染在 DOM 裡面的 HTML 寫 JavaScript,達到動態顯示不同的效果)(e.g. 使用者點擊登入後原本登入鍵就會變成登出鍵)。 ### 簡單的 JSX 應用 要記得 `return` JSX(也就是類似 HTML 的東西),背後會經過轉譯變成瀏覽器懂的 JavaScritp。 **需要注意不像 HTML ,如果 JSX 要使用 CSS class 要用 className,因為 class 在 JavaScript 是關鍵字不能使用。** ```htmlembedded= <div id="root"></div> ``` ```javascript= function MyName () { const myName = 'Lun'; return ( <p className="text-red">我的名字是 {myName}。</p> ) } ReactDOM.render(<MyName />, document.querySelector('#root')); ``` ![](https://i.imgur.com/BT0aQq8.png) *打開 Dev Tool 會發現已經被轉成一般的 HTML 以及 class* #### 可以直接寫 inline-style 可以宣告一個 inline-style 的物件,之後像一般的 HTML 一樣寫在標籤裡。 ```javascript= function MyName () { const textRed = { color: 'red' }; const myName = 'Lun'; return ( <p style={textRed}>我的名字是 {myName}。</p> ) } ReactDOM.render(<MyName />, document.querySelector('#root')); ``` 也可以**直接**寫在 style 裡面,但是記得要用兩個 `{{}}`。 ```javascript= function MyName () { const myName = 'Lun'; return ( <p style={{ color: 'red' }} >我的名字是 {myName}。</p> ) } ReactDOM.render(<MyName />, document.querySelector('#root')); ``` #### 既然支援 JavaScript,當然也可以使用陣列方法 目前先不要省略 `return`,熟悉了再省略。而且如果使用到變數的東西都要使用 `{}` 包起來。(e.g. {manga.map(....)}) ```javascript= function List () { const manga = ['異獸魔都', '進擊的巨人', '請叫我英雄']; return ( <ul> {manga.map(value => { return(<li>{value}</li>); })} </ul> ); } ReactDOM.render(<List />, document.querySelector('#root')); ``` ![](https://i.imgur.com/C8rOkhm.png) ## 基本 useState 的運用 重點關鍵字: > 這個方法之所以叫做 useState 是因為在 React 組件中,這些會連動導致畫面改變的「資料(data)」習慣上被稱作「狀態(state)」。以紅綠燈來說,假設有一個資料可以用來表示紅綠燈的顏色,0 是紅燈、1 是綠燈,當這個資料是 0 的時候,燈號就會變成紅燈的「狀態」;當資料變成 1 時,燈號就會變成「綠燈」的狀態。 > [*ref.:[Day 06 - 計數器] 醒醒啊!為什麼一動也不動 - useState 的基本使用*](https://ithelp.ithome.com.tw/articles/10219287) ==之後看到狀態(state)就可以想成是資料(data)理解。== **在第六篇中學習到使用 React 時如果想要把更新的資料渲染在頁面上就要使用 useState。** 用 Vanilla JS 的話就像之前寫的方式一樣,就不再贅述。[Vanilla JS 計數器](https://codepen.io/lun0223/pen/YzxMLoe) 新奇的地方是用 useState。useState 的名稱有 `use`,有 `use` 開頭的方法就代表它是一個 `Hook`。此外也有 `state(狀態)`代表這個方法有涉及到資料(data)相關的事情。 如果沒有使用 `useState`,那資料(data)(也就是狀態)有更新的話 React 本身並不會知道,所以即使值改變,但是畫面不會變。 ```javascript= const Content = () => { let count = 256; function add () { count ++; console.log(count); } return ( <div className="container"> {console.log('RENDER!')} <div className="chevron chevron-up" onClick={add} /> <div className="number">{count}</div> <div className="chevron chevron-down" /> </div> ) }; ReactDOM.render(<Content />, document.querySelector('#root')); ``` ![](https://i.imgur.com/rhbJUk6.png) *即便 `count` 的值有更新,但是 React 並不會知道這個值改變了,所以不會更新畫面(而且只會 render 一次)* 此時使用 useState 方法,React 就會知道值改變了,並且會渲染頁面。 **`useState` 回傳的函式通常會以 `setXXX => XXX 為基準值的代號,比方來說 count,所以函式就是 setCount` 提高程式碼閱讀性。** 使用步驟: 1. 先解構方法(取出) 2. 再用陣列結構提高使用的方便性 3. `useState(256)` 代表起始值(count)為 256 4. 之後在 `add` 函式中叫用並傳入 Arguments `setCount(count + 1)` 5. 事件(`onClick`)觸發函式,執行任務 ```javascript= // 1. 解構取出該方法 const { useState } = React; const Content = () => { // 2. 陣列結構取出叫用 useState 後得到的值 const [count, setCount] = useState(256); function add () { // 3. 叫用叫用 useState 後得到的函式並傳入 Argument(藉由陣列解構所以 setCount 會指向該函式的記憶體所以我可以叫用) setCount(count + 1); } function minus () { setCount(count - 1); } return ( <div className="container"> {console.log('RENDER!')} <div className="chevron chevron-up" onClick={add} /> <div className="number">{count}</div> <div className="chevron chevron-down" onClick={minus} /> </div> ) }; ReactDOM.render(<Content />, document.querySelector('#root')); ``` ![](https://i.imgur.com/MzNNVZ7.png) *這樣子當狀態(state)有更動,React 也會知道,所以就會渲染畫面(狀態改變就會 `console.log('RENDER!')`)* ### 為什麼要陣列結構? 增加使用時的便利性及程式碼閱讀性,因為 `useState()` 被叫用後就會回傳一個陣列,第一個值代表基準值,第二個值是一個函式,會以基準值來執行觸發事件要做什麼以及背後會自動觸發 re-render。 如果不用陣列結構的話就會有點麻煩: ```javascript= const { useState } = React; const Content = () => { const counting = useState(256); console.log(counting); // (2) [256, ƒ] f 代表函式 const count = counting[0]; // 就要很土法煉鋼地用 indexNum const setCount = counting[1]; function add () { setCount(count + 1); } function minus () { setCount(count - 1); } return ( <div className="container"> {console.log('RENDER!')} <div className="chevron chevron-up" onClick={add} /> <div className="number">{count}</div> <div className="chevron chevron-down" onClick={minus} /> </div> ) }; ReactDOM.render(<Content />, document.querySelector('#root')); ``` ### 為什麼用 `const [count, setCount] = useState(256)` 時可以更改常數 `count`? 一開始我用 `setCount(count ++)` 竟然報錯。 ```javascript= const { useState } = React; const Content = () => { const [count, setCount] = useState(256); function add () { // 看範例使用 +1 就想說用 ++ 應該沒差 setCount(count ++); } function minus () { setCount(count - 1); } return ( <div className="container"> {console.log('RENDER!')} <div className="chevron chevron-up" onClick={add} /> <div className="number">{count}</div> <div className="chevron chevron-down" onClick={minus} /> </div> ) }; ReactDOM.render(<Content />, document.querySelector('#root')); ``` ![](https://i.imgur.com/kNCR78s.png) *結果報錯了,因為陣列結構後 `count` 確實是由 `const` 宣告的常數,因為 `count ++` 代表的是 `count = count + 1` 但因為值(256)是基本型別(primitive type)所以是不能改變的(immutable)* **但是為什麼畫面還會改變啊囧!!!!!** 一開始我也是想不透為什麼,後來在網路上找到有大神解釋背後的概念: [*ref.:Why React Hook useState uses const and not let*](https://stackoverflow.com/questions/58860021/why-react-hook-usestate-uses-const-and-not-let) >Not really. When the component is rerendered, the function is executed again, creating a new scope, creating a new count variable, which has nothing to do with the previous variable. >Note: Hooks are way more sophisticated and are not actually implemented like this. This is just to demonstrate a similar behavior. ```javascript= let _state; let _initialized = false; function useState(initialValue) { if (!_initialized) { _state = initialValue; _initialized = true; } return [_state, v => _state = v]; } function Component() { const [count, setCount] = useState(0); console.log(count); setCount(count + 1); } Component(); Component(); // in reality `setCount` somehow triggers a rerender, calling Component again Component(); // another rerender ``` 看了大神的分享仔細思考其概念: 1. 初始化後就不會再初始化了(也就是 initialValue = _state) 2. useState() 回傳陣列中的第二個值(也就是函式)其實是改變外層變數的值,也剛好陣列中第一個值的記憶體是指向外層變數 3. 但是外層變數是 `let` 宣告的,所以同個變數名稱可以重新和值的記憶體連結,不過 `const` 指向的其實是外層變數的上一個值的記憶體,不過我們沒有使用同個變數名稱,所以不會錯 4. 然後當第二個函式被叫用的時候又會觸發 render,所以就會創造新的範疇 **實際操作** 除了第一次 Component Render 外,當事件觸發 `setCount(count + 1)` 時確實又 re-render Component 了。不過這時候又發現僅管我 `console.log(count)` 寫再後面,但是 `setCount(count + 1)` 會比較晚執行(雖然我目前還不知道為什麼,但是至少知道背後一部份的原理),之後說不定就會明白了。 ```javascript= // 1. 解構取出該方法 const { useState } = React; const Content = () => { const [count, setCount] = useState(256); function add () { setCount(count + 1); console.log(count); } function minus () { setCount(count - 1); } return ( <div className="container"> {console.log('RENDER!')} <div className="chevron chevron-up" onClick={add} /> <div className="number">{count}</div> <div className="chevron chevron-down" onClick={minus} /> </div> ) }; ReactDOM.render(<Content />, document.querySelector('#root')); ``` ![](https://i.imgur.com/6A7ZYAm.png) ### 聰明的 React 也只會更新必要的 Element React 背後有超猛函式確認更新的 Element 並只更新它,可以有效降低效能(好厲害!) [*ref.:React 只更新必要的 Element*](https://zh-hant.reactjs.org/docs/rendering-elements.html#react-only-updates-whats-necessary) ![](https://zh-hant.reactjs.org/c158617ed7cc0eac8f58330e49e48224/granular-dom-updates.gif) ## 參考資料 1. [從 Hooks 開始,讓你的網頁 React 起來 1 - 6](https://ithelp.ithome.com.tw/users/20103315/ironman/2668) 2. [使用 State Hook](https://zh-hant.reactjs.org/docs/hooks-state.html) 3. [Learn useState In 15 Minutes - React Hooks Explained](https://www.youtube.com/watch?v=O6P86uwfdR0&t=834s) 4. [Full React Tutorial #8 - Using State (useState hook)](https://www.youtube.com/watch?v=4pO-HcG2igk) 5. [React JSX](https://www.w3schools.com/react/react_jsx.asp)