iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
Modern Web

Follow me! 來一場前端技能樹之旅系列 第 20

[Day 20 - React] 網頁UI組件化 — React component

  • 分享至 

  • xImage
  •  

前情提要:在上一篇現在開始用框架寫網頁 — React,我們學習了如何在 React 使用 JSX 撰寫 Element 呈現出網頁頁面,接下來就會帶著大家將這些 React Element 抽離,建立 React 的核心 — Component,並且處理組件內部的資料。

建立 React component

直接利用上一篇文章的 Codesandbox React 專案,我們會實作做一個簡單的計數器。在上篇文章的最後,提到 index.js 裡面的 <App /> 就是一個 Component,組件的內容就實作在 app.js 裡。直接修改 app.js 內部的 React Element,呈現出計數器的畫面:

App.js

export default function App() {
  return (
    <div className="App">
      <h1>Member Score</h1>
      <div className="member">
        <h2>May</h2>
        <h3>0</h3>
        <button>Plus</button>
        <button>Minus</button>
      </div>
    </div>
  );
}

組件的內容被包裹在函式內部,這被稱作是 Function Component。在 index.js 引入在別份程式碼 export 的 Component 後,就會接收它所回傳的 React Element 渲染在網頁上。

接著可以將記錄成員的資料和計數器的網頁元素抽離,新增 Member.js ,拆離上面的 member 區塊變成一個新的 Component。

Member.js

export default function Member() {
  return (
    <div className="member">
      <h2>May</h2>
      <h3>0</h3>
      <button>Plus</button>
      <button>Minus</button>
    </div>
  );
}

拆離 Component 並且將它 export 後,要做的事就是在 app.js 引入它。

import Member from "./Member";

export default function App() {
  return (
    <div className="App">
      <h1>Member Score</h1>
      <!-- 由使用者定義 component 的 element  -->
      <Member />
    </div>
  );
}

我們可以隨意將網頁內容劃分成無數個 Component,不過仍然要遵守拆解的原則,讓 Component 只會負責處理與該組件有關的事情。另外你還可以將 Component 拆成更小 Component,比如 Member 內的計數器,就能抽離成 Counter Component。

Counter.js

export default function Counter() {

  return (
    <div className="counter">
      <h3>0</h3>
      <button>Plus</button>
      <button>minus</button>
    </div>
  );
}

Member.js

import Counter from "./Counter";

export default function Member() {
  return (
    <div className="member">
      <h2>May</h2>
      <Counter />
    </div>
  );
}

透過 Prop 傳遞組件資料

介面組件化的優點之一就是提升重複使用性,比如現在想要有三位成員的分數資料,只要複製三個 <Member /> 組件,就可以呈現出同樣的畫面,但同時三個組件會是獨立的,擁有各自的組件資料

App.js

export default function App() {
  return (
    <div className="App">
      <h1>Member Score</h1>
      <Member />
      <Member />
      <Member />
    </div>
  );
}

但問題是,現在三個 Component 的名字都是 May,我們希望能夠顯示成員各自的名字。React 就提供一個方法,可以將資料變成物件在 Component 間傳遞,這個物件就被稱作「props」。舉例來說,只要在 <Member /> 加上 name='名字' 屬性,{name='名字'} 就會作為 props 傳入到 Member Component 內。

App.js

export default function App() {
  return (
    <div className="App">
      <h1>Member Score</h1>
      <Member name='May'/>
      <Member name='Selina'/>
      <Member name='Julia'/>
    </div>
  );
}

分別設定好每個組件 props 的值,直接在 Member Component 接收 props 變數使用,就能顯示出各自的名字。

Member.js

import Counter from "./Counter";

export default function Member(props) {
  return (
    <div className="member">
      <h2>props.name</h2>
      <Counter />
    </div>
  );
}

加入 Local State 到組件

計數器的部分,兩個 Button 可以控制分數的加減,並且要讓Component 能夠記錄並更新分數,我們需要將分數存放到 State 中,當 render() 內部的 State 值更新,React 就會相對地更新網頁內容。

要在 Function Component 使用 State,會使用到 Hook 功能 useState,它會回傳目前 state 數值,和可以讓你更新 State 的 Function,同時要設定初始值。

Counter.js

import { useState } from "react";

export default function Counter() {
  //宣告一個 state 變數 score,初始值為 0
  const [score, setScore] = useState(0);

  return (
    <div className="Counter">
      <h3>{score}</h3>
      ...
    </div>
  );
}

現在要利用 useState 的 State Function,試著讓 State 隨著我們的輸入來更新。在兩個 Button 分別加上 Click 事件,按下 Plus 就讓計數器的值加一,按下 Minus 就減一:

Counter.js

export default function Counter() {
  ...
  return (
    <div className="Counter">
      ...
      <button onClick={() => setScore(score + 1)}>Plus</button>
      <button onClick={() => setScore(score - 1)}>minus</button>
    </div>
  );
}

完成後試著按一按每個成員的計分器,你可以發現每個 Component 就能擁有自己的 State,分別成功記錄每位成員的分數。


小結

大致了解 React 最重要的核心 — Component,我們現在可以自定義網頁元件,並在組件內控制資料的處理。但 React 能做到的不會只有可將網頁介面拆成獨立的組件、提升重用性,下一個章節就再來探討 React 其他更便利的功能,包括條件 Render、生成列表,以及如何提升 Component 的資料層級。

範例程式碼

如果文章中有錯誤的地方,要麻煩各位大大不吝賜教;喜歡的話,也要記得幫我按讚訂閱喔❤️

參考資料


上一篇
[Day 19 - React] 現在開始用框架寫網頁 — React
下一篇
[Day 21 - React] 今晚我想來點,React的其他功能
系列文
Follow me! 來一場前端技能樹之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言