讀者進入以下網址:https://codesandbox.io/s/new,就能進到由 CodeSandbox 提供的線上開發環境以及使用 create-react-app 指令建立好的 React 專案來進行練習
基本上每個元件是互相獨立的,但父元件可以傳遞資料給子元件,子元件則不能傳遞資料給父元件,以保持資料的傳遞方向是由上而下,如果某個元件的資料發生問題了那一定是本身的資料有問題或是由其父元件傳下來的資料有問題,而不會受到其子元件的資料影響,這就是單向資料流(unidirectional data flow)的概念。
使用元件是透過標籤的方式,因此父元件就利用標籤屬性來傳遞資料給子元件;而建立元件則是透過宣告函式的方式,因此子元件便是利用函式參數來接收資料:
// 父元件利用屬性傳遞資料給子元件
<子元件 名稱1=資料1 資料2=資料2 ... />
// 子元件利用函式參數接收資料
function 子元件(props) {
// props 會是
// {
// 名稱1: 資料1,
// 名稱2: 資料2,
// ...
// }
}
注意:為了避免傳遞多組資料時,造成子元件取得資料困難,一組資料並非對應到一個函式參數,而是將所有資料都對應到一個物件參數,我們則從此物件取得資料,React 官方稱此物件為 props,因此習慣上都將此參數名稱設為 props。
// 若一組資料對應到一個函式參數,多組資料對應到多個函式參數,造成參數數量過長或是參數順序的問題
function 子元件(名稱1, 資料2, ... ) {}
接續上一篇文章,我們將結構類似的 JSX 元素區塊提取出來包裝成元件,把原本龐大的單一元件切分成許多較小的原件,但並沒有發揮元件的重複利用性,我們的目標便是要利用 props 將類似的原件合而為一並且拿來重複利用。
目前 index.js
中的程式碼內容為:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function List1() {
return (
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
);
}
function List2() {
return (
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
);
}
function Block1() {
return (
<div>
<p>React 具有以下特性:</p>
<List1 />
</div>
);
}
function Block2() {
return (
<div>
<p>三大前端框架</p>
<List2 />
</div>
);
}
function App() {
return (
<div>
<h1>React</h1>
<Block1 />
<Block2 />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
我們看到其中 List1 和 List2 這兩個結構類似的元件:
function List1() {
return (
<ul>
<li>Virtual DOM 的機制</li>
<li>元件化的開發</li>
</ul>
);
}
function List2() {
return (
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
);
}
它們都有多組 li 元素,每一組 li 元素包含一段文字內容,我們可以宣告一個 array 來儲存這些文字內容,再將這個文字的 array 的轉換成 li 元素的 array,再嵌入 ul 元素中。
以 List1 元件為例:
function List1() {
const texts = ['Virtual DOM 的機制', '元件化的開發'];
const liElement = texts.map(text => <li>{text}</li>);
return (
<ul>
{liElement}
</ul>
);
}
如果看不懂這一行語法的讀者,可以參考前面一篇文章介紹的箭頭函式
我們可以進一步直接將變數 liElement 的值取代掉該變數:
function List1() {
const texts = ['Virtual DOM 的機制', '元件化的開發'];
return (
<ul>
{texts.map(text => <li>{text}</li>)}
</ul>
);
}
用同樣的方式將 List2 元件改寫,結果如下:
function List2() {
const texts = ['React', 'Vue', 'Angular'];
return (
<ul>
{texts.map(text => <li>{text}</li>)}
</ul>
);
}
改寫完之後,List1 和 List2 元件的 JSX 元素結構組成就會完全一樣,差別只在於 texts 這一組 array 型態的資料,我們傾向改由它們的父元件 Block1 和 Block2 來傳遞這一組資料,然後在元件內透過 props 取得,如此一來 List1 和 List2 這兩個元件就可以合而為一成 List 元件:
function List(props) {
const { texts } = props;
return (
<ul>
{texts.map(text => <li>{text}</li>)}
</ul>
);
}
function Block1() {
const texts = ['Virtual DOM 的機制', '元件化的開發'];
return (
<div>
<p>React 具有以下特性:</p>
<List texts={texts} />
</div>
);
}
function Block2() {
const texts = ['React', 'Vue', 'Angular'];
return (
<div>
<p>三大前端框架</p>
<List texts={['React', 'Vue', 'Angular']} />
</div>
);
}
讀者們可以進一步練習將 Block1 和 Block2 元件合而為一成 Block 元件,附上最後的程式碼:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function List(props) {
const { texts } = props;
return (
<ul>
{texts.map(text => <li>{text}</li>)}
</ul>
);
}
function Block(props) {
const { title, texts } = props;
return (
<div>
<p>{title}</p>
<List texts={texts} />
</div>
);
}
function App() {
return (
<div>
<h1>React</h1>
<Block title={"React 具有以下特性:"} texts={['Virtual DOM 的機制', '元件化的開發']} />
<Block title={"三大前端框架"} texts={['React', 'Vue', 'Angular']} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
希望讀者們看完本篇文章,能夠了解如何應用 props 來發揮元件的重複使用性,下一篇文章將介紹元件的另一種資料來源 - state。