# React 文件閱讀 ###### tags: `Javascript, React` # 介紹 JSX * 在 JS 裡面寫 html 這個語法叫做 JSX,是一個 JavaScript 的語法擴充 * JSX 允許你使用 JavaScript 所有的功能 * 執行 JSX 會產生 React「element」。 ```javascript= const element = <h1>你好,世界!</h1>; ``` ## 在 JSX 中嵌入 Expression 注意 JSX 如果拆成很多行表達,會建議要將多行的 JSX 放在括號內避免自動分號補足 (ASI) 你可以在 JSX 的大括號中寫入任何合法的 JavaScript expression。 例如 * 變數 * 2 + 2 * user.firstname * formatName(user) ```javascript= function formatName(user) { return user.firstName+ ' ' + user.lastName; } const user = { firstName: 'Harper', lastName: 'Perez' }; const element = ( <h1> Hello, {formatName(user)}! </h1> ); ReactDOM.render( element, document.getElementById('root') ); ``` ## JSX 本身也是 Expression 在編譯過後 JSX 就會變成一般的 function 呼叫並且回傳物件,代表著可以將其當作參數使用指定到一個變數之中,也可以用在 if , for 的迴圈之中 ```javascript= function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; } ``` ## 在 JSX 中指定屬性(attribute) 你可以使用**引號**將字串設定為屬性: (主要使用在字串屬性) ```javascript= const element = <div tabIndex="0"></div>; ``` 你也可以在屬性中使用**大括號**來嵌入一個 JavaScript expression:(主要使用在 JS expression) ```javascript= const element = <img src={user.avatarUrl}></img>; ``` 避免同時使用這兩者 camelCase 在 JSX 之中,class 變成了 className 而 tabindex 變成了 tabIndex。 ## 在 JSX 中指定 Children 針對內容為空的標籤也可以向 html tag 一樣直接 / 關閉標籤不須後面加一個關閉標籤 ```javascript= const element = <img src={user.avatarUrl} />; ``` JSX 標籤也可以包含 children:(應該是可以寫成巢狀的意思) ```javascript= const element = ( <div> <h1>Hello!</h1> <h2>Good to see you here.</h2> </div> ); ``` ## JSX 防範注入攻擊 這邊不理解 ## JSX 表示物件 下面兩段程式碼是完全相同的內容 ```javascript= const element = ( <h1 className="greeting"> Hello, World! </h1> ); ``` ```javascript= const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, World!' ); ``` React.createElement() 會產生 React element(會是物件的型別) ```javascript= // 注意:這是簡化過的結構 const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }; ``` 可以發現上述程式碼都在描述這個 element 的狀態,React 函式庫會讀取這些資料並且產生 DOM 並且保持在最新的狀態呈現在頁面上 提示: 我們推薦你在編輯器中使用 「Babel」語法,這樣可以確保 ES6 跟 JSX 都能夠正確的被語法突顯。 # Render Element > 建立 React 應用程式最小的單位是 element。 ```javascript= const element = <h1>Hello, world</h1>; ``` * React element 是單純的 object,而且很容易被建立。 * React DOM 負責更新 DOM 來符合 React element。 需要注意的地方是 component 是由 element 構成的兩者不一樣 ## Render Element 到 DOM 內 通常你會在 index.html 頁面找到這個 root ,會是掛載整個 React component 的地方 ```html= <div id="root"></div> ``` 使用 ReactDOM.render() 做操作,第一個參數是要渲染的資料,第二個是掛載的地方 ```javascript= const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root')); ``` 在網頁上你會看見顯示「Hello, world」。 ## 更新被 Render 的 Element React element 是不能被改變的 所以唯一更新 UI 的方式就是建立一個新的 element 藉由 setInterval 的方式每秒重新呼叫一次函式 tick 創造新的 element 來更新 UI ```javascript= function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render(element, document.getElementById('root')); } setInterval(tick, 1000); ``` ## React 只更新必要的 Element > React DOM 會將 element 和它的 children 與先前的狀態做比較,並且只更新必要的 DOM 達到理想的狀態。 ![](https://i.imgur.com/2gu98YI.gif) # Components 與 Props > Component 使你可以將 UI 拆分成獨立且可複用的程式碼,並且專注於各別程式碼的思考。 Component 就像是 JS 內的 functions 可以接收任意的參數在 React 內稱為 props ## Function Component 與 Class Component Function Component ```javascript= function Welcome(props) { return <h1>Hello, {props.name}</h1>; } ``` Class Component ```javascript= class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } } ``` 上述兩種 component 在 React 中是同等的。 ## Render 一個 Component 這段程式碼會在頁面上 render 出「Hello, Sara」: ```javascript= function Welcome(props) { return <h1>Hello, {props.name}</h1>; } const element = <Welcome name="Sara" />; ReactDOM.render( element, document.getElementById('root') ); ``` 經歷的過程會是這樣: 1. 使用了 `RenderDOM.render()` 這個函式準備印出 element 這個變數到 root 上 2. React 發現這個 element 的內容是個自定義的 tag 便去跑 Welcome function 3. React 以 `{name: 'Sara'}` 作為 props 傳入 Welcome function 並執行 4. Welcome function 回傳了 `<h1>Hello, Sara</h1>` 這個 element 作為返回值 5. React DOM 把 DOM 更新為 `<h1>Hello, Sara</h1>` 注意: Component 的字首須為大寫字母 ## 組合 Component > 簡單來說 Component 可以在其中使用其他 Components 的功能 比方說我們要在 Home component 中使用 按鈕、表單、對話框等等 components 舉例來說,我們可以建立一個 render 多次 Welcome 的 App component: ```javascript= function Welcome(props) { return <h1>Hello, {props.name}</h1>; } function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') ); ``` ## 抽離 Component 簡單來說就是讓 Component 的內容再抽出來形成其他的 Component 除了增加可讀性,也讓重複利用的可能性增加 下面是一個複雜的 Component ```javascript= function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } ``` 首先, 我們將 Avatar 分離出來形成一個 Component ```javascript= function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } ``` 建議從 component 的角度為 props 命名,而不是它的使用情境。 因為 Avatar 已經被抽出來了所以直接使用其 tag 加上新的命名的 props 即可 ```javascript= function Comment(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> // 主要改動在這變成只有一行且更好閱讀並且可以重複利用在其他地方 <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } ``` 將 UserInfo component 也抽離出來並且放入 Avatar tag ```javascript= function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); } ``` 此時的 Comment 更加簡化了 ```javascript= function Comment(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } ``` ## Props 是唯讀的 > 不管你使用 function 或是 class 來宣告 component,都絕不能修改自己的 props。 下方的範例 並沒有改變輸入 a b 一樣是自己 ```javascript= function sum(a, b) { return a + b; } ``` 下方範例則改變 amount 的結果 ```javascript= function withdraw(account, amount) { account.total -= amount; } ``` > 所有的 React component 都必須像 Pure function 一般保護他的 props 所以就要靠 **State** 來做出動態的改變 可以在不違反上述規則的前提下,讓 React component 隨使用者操作、網路回應、或是其他方式改變輸出內容。 # State 和生命週期 > 這個章節會介紹在 React component 中 state 以及生命週期的概念。 前面章節只有介紹到 ReactDOM.render 這個 更新 UI 的方式,這個章節會使用這個 clock 範例並操作 State 以及 其他生命週期的 hook 來實作這個 clock 藉著 setInterval tick函式每秒會被呼叫一次因此 ReacDOM.render 也會因此更新一次畫面來達到 clock 的效果 ```javascript= function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000); ``` ## 首先我們會針對程式碼做簡化 使用 props 的方式傳遞 new Date() 函式 ```javascript= function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000); ``` 一般的時鐘程式會含有計時器以及每秒更新 UI 所以理想的狀態式我們只要寫一次下方的程式碼,時鐘就該自己跑起來,為此我們加得加進去 State ```javascript= ReactDOM.render( <Clock />, document.getElementById('root') ); ``` ## Converting a Function to a Class 首先我們把整個 component 轉變成 class ```javascript= class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } ```