Material UI
使用Material UI為React設計版面
2019/09/30
2019/10/06 (增加連結)
2020/07/05 (改寫)
React + Material-UI(Tables)
這次接續使用上一章的範例 (教學: Functional Component、Form ),示範如何用Material-UI的「Tables 」模組將資料顯示在頁面上。Material Design是Google開發的設計語言,Material-UI就是一些Material Design的react模組/元件。
Install Material-UI
在Material-UI的官網有安裝指令,我們直接使用npm指令安裝 :
// 用npm安装
npm install @material-ui/core
感謝伍庭儀學姊撰寫這部分的教材
Table Component
官網的左側導覽列中,點擊Components裡的裡的Tables項目。這裡有很多種功能的表格類型,我們先從最簡單的Simple Table 做練習。
範例下方的按鍵打開可以看見範例代碼:
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
const useStyles = makeStyles(theme => ({
root: {
width: '100%',
marginTop: theme.spacing(3),
overflowX: 'auto',
},
table: {
minWidth: 650,
},
}));
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
function SimpleTable() {
const classes = useStyles();
return (
<Paper className={classes.root}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
);
}
export default SimpleTable;
import
最上面的部分import所需使用的模組
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
makeStyles()
Material-UI用來定義頁面樣式的hook函式。
const useStyles = makeStyles(theme => ({
root: {
width: '100%',
marginTop: theme.spacing(3),
overflowX: 'auto',
},
table: {
minWidth: 650,
},
}));
React 特別之處就在於它可以全部使用JavaScript去撰寫網頁,包含html、CSS 跟 Script(雖然這部分本來就是用JS寫的啦哈哈哈)的部分,我們可以統一使用JS定義完成。這種使用JS定義CSS的方法稱為——「 CSS in JS 」(JSS)
當然我們也可以透過import CSS檔,用傳統的方式撰寫網頁樣式,但如果使用了Material-UI,還是建議使用JSS的方式定義樣式,即便要將所有樣式集中到同一個檔案裡管理,也還是可以使用JSS的方式定義樣式。
另外,JSS要特別注意的地方是,由於JS用Object的概念定義樣式,所以有些地方的格式跟CSS不太一樣:
樣式名稱不用 ' - ' 符號連結而是駝峰式命名。例:overflowX: 'auto',原本在CSS中是 over-flow 的寫法,這裡把連結符號刪掉,第二個單字的第一個字母大寫,變成 overFlow
樣式的內容為需為變數或字串。例:width: '100%',原本在CSS 100%的數值需要加上' ' 或 " "(JS中兩著皆可);如數值不加單位例:minWidth: 650,預設單位則為 px,即 min-width 等於 650px。
樣式換行結尾使用 ' , '符號。與一般CSS用分號不同,JSS用的是逗號。
createData()
創建文件資料。這部分不會使用於此次範例中。
function createData(name, calories, fat, carbs, protein) {
return { name, calories, fat, carbs, protein };
}
const rows = [
createData('Frozen yoghurt', 159, 6.0, 24, 4.0),
createData('Ice cream sandwich', 237, 9.0, 37, 4.3),
createData('Eclair', 262, 16.0, 24, 6.0),
createData('Cupcake', 305, 3.7, 67, 4.3),
createData('Gingerbread', 356, 16.0, 49, 3.9),
];
Function Component(SimpleTable())
輸出組件頁面與嵌入資料陣列。
function SimpleTable() {
const classes = useStyles();
return (
<Paper className={classes.root}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<TableRow key={row.name}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
);
}
export default SimpleTable;
Functional Components
我們來為我們在Functional Component、Form 的範例加上Material-UI吧!
在src\product\productList.js裡,用到了table,我們來改用Material-UI的Table吧! 我們先參考Simple Table。(詳參: Table API)
先加上該import的內容。
import ProductAdd from './ProductAdd'
import React, {useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
設定style (詳參: @material-ui/styles),這邊的語法不是CSS的語法,而是CSS in JS,或者稱為JSS。(詳參: Objects based styles syntax for declaring Style Sheets、JSS integration with React)
const useStyles = makeStyles({
table: {
minWidth: 650,
width: 'auto',
margin: 'auto',
},
})
export default function ProductList() {
const classes = useStyles();
表格內容:
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>#</TableCell>
<TableCell align="left">產品描述</TableCell>
<TableCell align="right">價格</TableCell>
<TableCell align="left">產品類型</TableCell>
<TableCell align="right">庫存量</TableCell>
<TableCell align="right">安全存量</TableCell>
</TableRow>
</TableHead>
<TableBody>
{products.map((product, index) => (
<TableRow key={index}>
<TableCell component="th" scope="row">
{index}
</TableCell>
<TableCell align="left">{product.desc}</TableCell>
<TableCell align="right">{product.price}</TableCell>
<TableCell align="left">{product.category}</TableCell>
<TableCell align="right">{product.inventory}</TableCell>
<TableCell align="right">{product.safetyStock}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<ProductAdd update={update}/>
</TableContainer>
);
完整的內容
src\product\productList.js
//import React from 'react';
import ProductAdd from './ProductAdd'
import React, {useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
const useStyles = makeStyles({
table: {
minWidth: 650,
width: 'auto',
margin: 'auto',
},
})
export default function ProductList() {
const classes = useStyles();
const [products, setProducts] = useState([
{desc:"iPad", price:20000, category:"平板電腦", inventory: 15, safetyStock: 10},
{desc:"iPhone X", price:30000, category:"智慧型手機", inventory: 50, safetyStock: 30}
]);
function update(newProduct){
setProducts(oldProducts=>[...oldProducts, newProduct]);
}
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>#</TableCell>
<TableCell align="left">產品描述</TableCell>
<TableCell align="right">價格</TableCell>
<TableCell align="left">產品類型</TableCell>
<TableCell align="right">庫存量</TableCell>
<TableCell align="right">安全存量</TableCell>
</TableRow>
</TableHead>
<TableBody>
{products.map((product, index) => (
<TableRow key={index}>
<TableCell component="th" scope="row">
{index}
</TableCell>
<TableCell align="left">{product.desc}</TableCell>
<TableCell align="right">{product.price}</TableCell>
<TableCell align="left">{product.category}</TableCell>
<TableCell align="right">{product.inventory}</TableCell>
<TableCell align="right">{product.safetyStock}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<ProductAdd update={update}/>
</TableContainer>
);
}
productAdd用到Text Field、Button、Select。(詳參: Button API、TextField API、Select API)
Button
<Button variant="contained" color="primary" onClick={() => props.update({desc,price,category, inventory, safetyStock})}>
新增
</Button>
也可以利用className來套用style。
const useStyles = makeStyles((theme) => ({
button: {
background: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
margin: theme.spacing(1),
},
}));
寫法:
<Button className={classes.button} onClick={() => props.update({desc,price,category, inventory, safetyStock})}>
新增
</Button>
另外,也可以使用JSS的Pseudo and Nested Selectors,以及CSS Selector Reference。
& > *,表示root下的所有元件,設定這些元件的margin及width
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
margin使用了material預設主題的spacing設定 (詳參: Spacing),width: '25ch' 代表25個"0"的寬度 (詳參: CSS Units)。將這樣的style套到form裡面,就會讓form裡的原件都有一致的margin跟width。
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
將root套用在form。
<form className={classes.root} noValidate autoComplete="off">
style的內容,要注意重複的問題。當我們加了formControl後,事實上,是跟root的設定重複了,因為,當我們使用的時候,是將formControl放在form底下,雖然,root的規則適用,但是,又被formControl蓋掉了。所以,我們會發現,改root底下的margin是沒用的。
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
所以,同樣的設定只要留一個就好,避免未來困擾自己。
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
button: {
background: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
margin: theme.spacing(1),
},
formControl: {
minWidth: 120,
},
}));
可以直接套用theme的預設值,theme的預設值可參考: Default Theme。
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
button: {
background: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
margin: theme.spacing(1),
},
formControl: {
minWidth: 120,
},
}));
有個小小的注意事項,onChange是當內容修改的時候才會去更動state。如果是這樣寫,會發現選擇"個人電腦"時,內容會是空的。
const [category, setCategory] = useState("");
要改成:
const [category, setCategory] = useState("個人電腦");
這樣的話,如果使用者沒有更動內容的話,才不會內容是空白。
完整的內容
src\product\productAdd.js
import React, {useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel';
import Select from '@material-ui/core/Select';
import TextField from '@material-ui/core/TextField';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
button: {
background: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
margin: theme.spacing(1),
},
formControl: {
minWidth: 120,
},
}));
export default function ProductAdd(props) {
const classes = useStyles();
const [desc, setDesc] = useState("");
const [price, setPrice] = useState("");
const [category, setCategory] = useState("個人電腦");
const [inventory, setInventory] = useState(0);
const [safetyStock, setSafetyStock] = useState(0);
const categories = ["個人電腦","筆記型電腦","平板電腦","智慧型手機"];
return (
<form className={classes.root} noValidate autoComplete="off">
<FormControl className={classes.formControl}>
<TextField id="desc" label="產品描述" value={desc} onChange={e => setDesc(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="price" label="價格" value={price} onChange={e => setPrice(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="filled-age-native-simple">產品類型</InputLabel>
<Select
native
value={category}
onChange={e => setCategory(e.target.value)}
inputProps={{
name: 'category',
id: 'category',
}}
>
{categories.map((category, index) => (
<option key={index} value={category}>{category}</option>
))}
</Select>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="inventory" label="庫存量" value={inventory} onChange={e => setInventory(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="saftyStock" label="安全存量" value={safetyStock} onChange={e => setSafetyStock(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<Button className={classes.button} onClick={() => props.update({desc,price,category, inventory, safetyStock})}>
新增
</Button>
</FormControl>
</form>
);
}
目前我們寫了兩個元件,都在元件中各自定義style,比較好的方式是共用同樣的style。當元件很多的時候,這樣的寫法就不是最好的寫法,還有別的寫法,後面會再介紹。
先新增一個src/product/style.js
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
table: {
minWidth: 650,
width: 'auto',
margin: 'auto',
},
button: {
background: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
margin: theme.spacing(1),
},
formControl: {
minWidth: 60,
},
}));
export default useStyles;
ProductList加上import
import useStyles from './style';
因為ProductAdd被包含在ProductList裡,如果也是利用import,style會衝突,所以,將ProductList的style利用prop傳給ProductAdd。
<ProductAdd classes ={classes} update={update}/>
src/product/ProductList.js
import ProductAdd from './ProductAdd'
import React, {useState} from 'react';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import useStyles from './style';
export default function ProductList() {
const classes = useStyles();
const [products, setProducts] = useState([
{desc:"iPad", price:20000, category:"平板電腦", inventory: 15, safetyStock: 10},
{desc:"iPhone X", price:30000, category:"智慧型手機", inventory: 50, safetyStock: 30}
]);
function update(newProduct){
//products.push(newProduct);
//setProducts(products);
console.log(newProduct);
setProducts(oldProducts=>[...oldProducts, newProduct]);
}
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>#</TableCell>
<TableCell align="left">產品描述</TableCell>
<TableCell align="right">價格</TableCell>
<TableCell align="left">產品類型</TableCell>
<TableCell align="right">庫存量</TableCell>
<TableCell align="right">安全存量</TableCell>
</TableRow>
</TableHead>
<TableBody>
{products.map((product, index) => (
<TableRow key={index}>
<TableCell component="th" scope="row">
{index}
</TableCell>
<TableCell align="left">{product.desc}</TableCell>
<TableCell align="right">{product.price}</TableCell>
<TableCell align="left">{product.category}</TableCell>
<TableCell align="right">{product.inventory}</TableCell>
<TableCell align="right">{product.safetyStock}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<ProductAdd classes ={classes} update={update}/>
</TableContainer>
);
}
利用props的classes
const classes = props.classes;
src/product/ProductAdd.js
import React, {useState} from 'react';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import InputLabel from '@material-ui/core/InputLabel';
import Select from '@material-ui/core/Select';
import TextField from '@material-ui/core/TextField';
export default function ProductAdd(props) {
const classes = props.classes; //useStyles in ProductList to prevent conflict
const [desc, setDesc] = useState("");
const [price, setPrice] = useState("");
const [category, setCategory] = useState("個人電腦");
const [inventory, setInventory] = useState(0);
const [safetyStock, setSafetyStock] = useState(0);
const categories = ["個人電腦","筆記型電腦","平板電腦","智慧型手機"];
return (
<form className={classes.root} noValidate autoComplete="off">
<FormControl className={classes.formControl}>
<TextField id="desc" label="產品描述" value={desc} onChange={e => setDesc(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="price" label="價格" value={price} onChange={e => setPrice(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="category">產品類型</InputLabel>
<Select
native
value={category}
onChange={e => setCategory(e.target.value)}
inputProps={{
name: 'category',
id: 'category',
}}
>
{categories.map((category, index) => (
<option key={index} value={category}>{category}</option>
))}
</Select>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="inventory" label="庫存量" value={inventory} onChange={e => setInventory(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<TextField id="saftyStock" label="安全存量" value={safetyStock} onChange={e => setSafetyStock(e.target.value)}/>
</FormControl>
<FormControl className={classes.formControl}>
<Button className={classes.button} onClick={() => props.update({desc,price,category, inventory, safetyStock})}>
新增
</Button>
</FormControl>
</form>
);
}
作業
試試看把新增的部分,改用Dialog,並利用Floating action button。
參考資料
A beginners guide to Material UI React tutorial
<AppBar>
<Grid> <Card>
<FormControl>
A Better Way to Style Material-UI?
Separate styling from the component by styling in one place.
Make the components clean as much as possible (contain only the logic).
They have to be easy to move around without a lot of works, however still present the same styles.
How to use styled components with Material UI in a React app
The 5 steps to mastery
Use Material UI’s JSS classes once for overriding Material UI original theme
Use the styled() method to style your Material UI components
Prioritize the CSS rules of your styled-components over those of the JSS
Dynamically style your components while avoiding warnings related to DOM attributes
Override other classes than root
Hooks
Class Components
先來改寫ProductList.js,Table裡包一個TableBody。
import React, {Component} from 'react';
import Product from './product';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
export default class ProductList extends Component {
state = {
products: [
{id:"0", desc:"iPad", price:20000},
{id:"1", desc:"iPhone X", price:30000}
]
}
render() {
return (
<Table>
<TableBody>
{ this.state.products.map((product, index) => <Product key ={key} product={product}/>)}
</TableBody>
</Table>
)
}
}
再來改寫Product.js,TableRow裡包三個TableCell。
import React, {Component} from 'react';
import TableCell from '@material-ui/core/TableCell';
import TableRow from '@material-ui/core/TableRow';
export default class Product extends Component{
render(){
return (
<TableRow>
<TableCell>{this.props.product.id}</TableCell>
<TableCell>{this.props.product.desc}</TableCell>
<TableCell> {this.props.product.price}</TableCell>
</TableRow>
)
}
}
這樣,我們套用了Material-UI的Table了。
我們來加上表頭,TableHead裡包三個TableCell。
import React, {Component} from 'react';
import Product from './product';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
export default class ProductList extends Component {
state = {
products: [
{id:"0", desc:"iPad", price:20000},
{id:"1", desc:"iPhone X", price:30000}
]
}
render() {
return (
<Table>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Desc</TableCell>
<TableCell>Price</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ this.state.products.map((product, index) => <Product key = {index} product={product}/>)}
</TableBody>
</Table>
)
}
}
Style
比起Functional Components,Class Component在這部分比較複雜一點。
這邊用到material提供的一個Higher Order Component, withStyles (Day 25 - 二周目 - React component 套件 Material-UI: Google Material Design 的實作套件),將styles當作props傳進ProductList,再重新命名為classes。
import React, {Component} from 'react';
import Product from './product';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';
import { withStyles } from "@material-ui/core/styles";
const styles = theme => ({
root: {
width: '85%',
marginTop: theme.spacing(3),
marginLeft: theme.spacing(3),
overflowX: 'auto', },
table: {
minWidth: 450,
},
});
class ProductList extends Component {
state = {
products: [
{id:"0", desc:"iPad", price:20000},
{id:"1", desc:"iPhone X", price:30000}
]
}
render() {
return (
<Paper className={this.props.classes.root}>
<Table className={this.props.classes.table}>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Desc</TableCell>
<TableCell>Price</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ this.state.products.map((product, index) => <Product key={index} product={product}/>)}
</TableBody>
</Table>
</Paper>
)
}
}
export default withStyles(styles) (ProductList);
可以將傳進來的props改個名稱
const { classes } = this.props;
完整的程式碼:
import React, {Component} from 'react';
import Product from './product';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import Paper from '@material-ui/core/Paper';
import { withStyles } from "@material-ui/core/styles";
const styles = theme => ({
root: {
width: '85%',
marginTop: theme.spacing(3),
marginLeft: theme.spacing(3),
overflowX: 'auto',
},
table: {
minWidth: 450,
},
});
class ProductList extends Component {
state = {
products: [
{id:"0", desc:"iPad", price:20000},
{id:"1", desc:"iPhone X", price:30000}
]
}
render() {
const { classes } = this.props;
//is equivalent to
//const classes = this.props.classes;
return (
<Paper className={classes.root}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>Desc</TableCell>
<TableCell>Price</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ this.state.products.map((product, index) => <Product key ={index} product={product}/>)}
</TableBody>
</Table>
</Paper>
)
}
}
export default withStyles(styles)(ProductList);
作業
請參考範例,改寫Class Component作業:
編號 / id (int)
產品描述 / desc (String)
價格 / price (int)
產品類型 / category (String) (個人電腦、筆記型電腦、平板電腦、智慧型手機)
庫存量 / inventory (int)
安全存量 / safetyStock (int)