# React Quick Start快速入門指引 這篇文快速介紹了入門階段需要攻略的 10 大核心觀念。這篇文章是給人當成目錄快速看過去的,把一些術語、語法大概的樣子記錄在腦海裡,之後每件事都有對應的專文討論。基本上此篇為[官方quick start](https://beta.reactjs.org/learn)的中文翻譯版本,作為我開始學習React的入門講義,也希望可以幫助到有需要的人。 Let's get started. ## Quick Start 歡迎來到React的官方導讀文件,在這篇內容裡面會介紹約有80%的React概念且也會是你日常使用上會用到的。 以下你會學習到: * How to create and nest components * How to add markup and styles * How to display data * How to render conditions and lists * How to respond to events and update the screen * How to share data between components ### Creating and nesting components React apps就是由components(組件)所組成的,Component就是一個有自有邏輯和外觀的UI(user interface),可以小如一個按鈕或是大至一整個頁面都可以算是一個component。 React components就是JavaScript函式而此函式會回傳標記語言(markup): ```jsx= function MyButton() { return ( <button>I'm a button</button> ); } ``` 現在你已經宣告了一個component`MyButton`,你可以將其置入在另外一個component中: ```jsx= export default function MyApp() { return ( <div> <h1>Welcome to my app</h1> <MyButton /> </div> ); } ``` 請特別注意到`<MyButton />`開頭為一個大寫,這就是你如何知道其為一個React component,React component的名稱永遠必須唯一大寫字母開頭,而HTML的標籤是小寫。 下面為結果 <iframe src="https://codesandbox.io/embed/peaceful-meninsky-ieh8uf?autoresize=1&fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="peaceful-meninsky-ieh8uf" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> `export default`特別指出了檔案中的主component,如果你對某些JS語法還不熟悉可以去MDN和javascropt.info查閱。 ### Writing markup with JSX 上面看到的註記語法叫做JSX,它可以是選擇性的但多數的React專案中都會用JSX來方便使用。 JSX比HTML還要嚴謹,你必須使用封閉tag像是`<br />`,還有你的components也無法回傳多個JSX tags,你必須要將它們包起來例如使用`<div>...</div>`或`<>...</>`包裝。 ```jsx= function AboutPage() { return ( <> <h1>About</h1> <p>Hello there.<br />How do you do?</p> </> ); } ``` 如果你有很多HTML tags需要轉換成JSX你可以使用這個[工具](https://transform.tools/html-to-jsx) ### Adding styles 在React中,你可以指定CSS class藉由`className`,就像是你在HTML中使用class屬性一樣: ```jsx= <img className="avatar" /> ``` 然後你可以在CSS檔案中寫css的設定: ```css= /* In your CSS */ .avatar { border-radius: 50%; } ``` React並沒有規定你如何加入CSS檔案,在最簡單的使用範例,可以將`<link>`tag加到你的HTML檔案中,如果你使用的是build tool或是框架,請再查閱他們的文件如何將CSS加入到你的專案中。 ### Displaying data JSX讓你將標記語言放到JavsScrit中,而大括號(Curly braces)讓你跳回JavaScript中,所以你可以在code中嵌入一些變數讓你展示資訊給使用者,例如以下,這個會展示user.name: ```jsx= return ( <h1> {user.name} </h1> ); ``` 這招也可以讓你使用在JSX的屬性中,你需要使用大括號`{}`而非引號`""`,例如,class="avatar"傳入的是"avatar"這個字串作為CSS的class,但src={user.imageUrl}讀的是JS的`user.imageUrl`變數值,並且傳入此值作為src的屬性。 ```jsx= return ( <img className="avatar" src={user.imageUrl} /> ); ``` 你也可以放入更複雜的表達方式,例如: ```jsx= const user = { name: 'Hedy Lamarr', imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg', imageSize: 90, }; export default function Profile() { return ( <> <h1>{user.name}</h1> <img className="avatar" src={user.imageUrl} alt={'Photo of ' + user.name} style={{ width: user.imageSize, height: user.imageSize }} /> </> ); } ``` 上面範例中`style={{}}`並非為特殊語法,而是一個普通的{}物件在style= { }裡面,你可以使用style屬性當你的style屬性取決於JS的變數時。 ### Conditional rendering 在React中,並沒有特殊的語法來表達條件式,反而是跟你在寫JavaScript的方式是一樣的,例如你可以在JSX中使用if陳述式: ```jsx= let content; if (isLoggedIn) { content = <AdminPanel />; } else { content = <LoginForm />; } return ( <div> {content} </div> ); ``` 如果你偏好比較簡短的code,你可以使用?運算符: ```jsx= <div> {isLoggedIn ? ( <AdminPanel /> ) : ( <LoginForm /> )} </div> ``` 當你不需要else的話,你甚至可以用更短的&&語法: ```jsx= <div> {isLoggedIn && <AdminPanel />} </div> ``` 以上這些方式也可以被使用在屬性中,如果你對於這些JavaScript語法不熟悉的話,你永遠可以都使用`if...else..`作為起步。 ### Rendering lists 你會依賴JavaScript的功能像是loop迴圈或是array map()來展現一堆的components。 例如以下,假如你有一堆產品要展示: ```jsx= const products = [ { title: 'Cabbage', id: 1 }, { title: 'Garlic', id: 2 }, { title: 'Apple', id: 3 }, ]; ``` 在你的component中使用map()函式來轉換產品陣列為一個陣列的`<li>`物件: ```jsx= const listItems = products.map(product => <li key={product.id}> {product.title} </li> ); return ( <ul>{listItems}</ul> ); ``` 請特別注意到`<li>`有一個key屬性,在列表中的每個物件你都應該要傳入一個字串或是數字來表達其獨特於其他的物件,通常來說key的值會是來自你的資料例如資料庫的id,React會根據其來了解後續你是否插入、刪除或是重新排序其物件。 <iframe src="https://codesandbox.io/embed/xenodochial-bell-xtczu1?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="xenodochial-bell-xtczu1" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> ### Responding to events 你可以藉由在components裡面宣告一個event handler函式來響應事件: ```jsx= function MyButton() { function handleClick() { alert('You clicked me!'); } return ( <button onClick={handleClick}> Click me </button> ); } ``` 請注意到`onClick={handleClick}`內的函式名稱後面並沒有括號(parentheses),你只需要將其傳入而並不需要呼叫,React會自動呼叫這個event handler當使用者點擊按鈕時。 ### Updating the screen 通常來說,你會想要你的component來記住一些資訊並且展示出來,例如你想要計算一個按鈕被點擊了幾次,為達到此目的,可以在component加上state。 首先,先從React內引用`useState` ```jsx= import { useState } from 'react'; ``` 再來在component內宣告一個state變數: ```jsx= function MyButton() { const [count, setCount] = useState(0); ``` 從`useState`會拿到兩個東西:一個是目前的state(`count`),另一個是更新state的function(`setCount`),你可以隨意命名,但慣例上來說會把他們叫做`[something, setSomething]`。 按鈕第一次展現時`count`會是0因為你傳了0到`useState()`內。當你想要改變state時,可以呼叫`setCount()`並且傳入新的值。 以下是點擊後會增加數字的計數器: ```jsx= function MyButton() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <button onClick={handleClick}> Clicked {count} times </button> ); } ``` 如果你嘗試多次render一樣的component,而每一個component都會有自己的state,試著點下面不同的按鈕 <iframe src="https://codesandbox.io/embed/blissful-galois-m26wrx?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="blissful-galois-m26wrx" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe> 請注意到每個按鈕都會記得自己被點擊的次數,且相互不影響。 ### Using Hooks 函式名稱開頭為`use`的都會被稱作Hooks,`useState`就是React提供內建的Hook,你可以在[React API reference](https://beta.reactjs.org/apis/react)找到更多內建的Hooks,你也可以根據現有的Hooks做結合寫出自己的Hook。 Hooks比起其他普通函式還更有限制,你只能在最上層的component呼叫Hooks或是在其他Hook中,如果你想要在條件判斷式、迴圈使用`useState`則需要另外建立新的component並且將其放在那裡。 ### Sharing data between components 在前一個範例中,各個`MyButton`都有自己獨立的`count`,且當各個按鈕被點擊,只有被點到的按鈕會有改變: ![](https://i.imgur.com/hPICvdc.png)![](https://i.imgur.com/Q394Hhp.png) 然而,有時你會想要components間共享資料以及同時更新。 為了讓兩個`MyButton`展示相同數字以及共同更新,你需要將state從個別的按鈕"向上"移動到最近的且包含這兩個component的母component。 ![](https://i.imgur.com/SFmw70Z.png) ![](https://i.imgur.com/gmrGGoH.png) 現在無論你點哪個按鈕,`Myapp`的`count`會有變化,也會同時改變兩個`MyButton`的數字,以下是你怎麼在code裡面達成這點。 ```jsx= function MyButton() { // ... we're moving code from here ... } export default function MyApp() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <div> <h1>Counters that update separately</h1> <MyButton /> <MyButton /> </div> ); } ``` 然後從`MyApp`傳state到各個`MyButton`,還有click handler也一起傳入,你可以透過JSX的大括號方式傳值到`MyButton`,就像你先前對`<img>`做的動作相同: ```jsx= export default function MyApp() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <div> <h1>Counters that update together</h1> <MyButton count={count} onClick={handleClick} /> <MyButton count={count} onClick={handleClick} /> </div> ); } ``` 你傳入的資訊被稱作為*props*,現在`MyApp` component包含了`count` state以及`handleClick` event handler,並將他們以*props*的形式傳到各個按鈕。 最後,修改`MyButton`compoent函式用以接收母component傳入的*props*: ```jsx= function MyButton({ count, onClick }) { return ( <button onClick={onClick}> Clicked {count} times </button> ); } ``` 當你點擊了按鈕,`onClick` handler會被觸發,各個按鈕的`onClick` prop已經被寫入了`MyApp`的`handleClick`,所以裡面的code會被運行。 會呼叫`setCount(count + 1)`,增加`count` state的變數,新的`count`數值也被傳入到各個按鈕中,所以他們會展現新的數字。 這個動作叫做"lifting state up",藉由向上移動state,我們就可以在component間分享資料以及同步了。 以下完整code: <iframe src="https://codesandbox.io/embed/cocky-fire-5lhkwc?fontsize=14&hidenavigation=1&theme=dark" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" title="cocky-fire-5lhkwc" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" ></iframe>