使用 Material-UI 的 component

回憶

前面三天都是在談 Redux 關於前端的資料流,今天要談一個「非必要」的 react component 套件:Material-UI。它是Material Design 的 react 實作套件,它用來提供畫面的組件 (component),像是 Button, Input, Dialog…等,充分地體現出 React component 讓我們以組件的概念組裝畫面。

我們曾在Day 12 - 二周目 - 準備起程深入後端 中提到設計師做完設計稿後,會由一個前端工程師,做「切版」的工作,一般會寫出 HTML + CSS + Javascript。若前端框架採用 React,我們會把他們割一割封裝在各個 component,最後用 component 組裝畫面。

然而,不是每家公司都有「切版」的工程師。若只是單純的做後台網站,沒有對廣大的用戶,就不會講求畫面多絢麗、使用者體驗多利害,所以就有這種已經做好的畫面樣式的 component 可以直接使用,像是:Material-UI(Google)Ant Design (螞蟻金服)…等。

目標

過程請見 github commit log

  1. 使用 Material-UI 的 component
  2. 如何對為 component 加入css 樣式
  3. 如何用 Material-UI Grid 排版

使用 Material-UI

Material-UI 功能特色:

  1. 大量的基本 component
  2. 樣式(css style)注入系統:withStyles 這個 HOC(high order component),可以注入/覆寫樣式
  3. 排版 component: Grid

安裝 Material-UI

Material-UI 歷經多次的 API 改版,現在的套件變的比較有組識了,基本元件全放到 @material-ui/core

  1. 複制 Day 23 - 二周目 - Redux 串接 React 與 Redux DevTools 的專案 hello-react
  2. 安裝 material-ui/core: 安裝 npm install @material-ui/core --save
  3. 安裝 material-ui/icons(可選): 安裝 npm install @material-ui/icons --save, 這套件提供一些svg(Scalable Vector Graphics) icon

大量的基本 component

體驗一下 Material-UI

我們之前在 hello-react 中加入了一個 button 來發出一個 action,如圖 Screen Shot 2018-10-24 at 2.11.33 PM.png

現在我們來換成 Material-UI Button。修改前試想一下,我們要改的是畫面,所以應該改哪個資料夾的檔案呢?actions/stores/reducers/components/containers 哪一個? 若你之前有跟上文章的內容應該可以想到,答應就是

1components

它是掌管畫面的資料夾,所以改 components/LoginBox.js

 1// components/LoginBox.js
 2import React, { Component } from 'react';
 3import Button from '@material-ui/core/Button';
 4
 5class LoginBox extends Component {
 6  render() {
 7    return (
 8      <div>
 9        message: {this.props.message}
10        <Button onClick={this.props.onClickSubmit}>Submit</Button>
11      </div >
12    );
13  }
14}

我們把 html tag button,換成是 Material-UI component Button。存檔後就完成了,看結果 Screen Recording 2018-10-24 at 2.20.58 PM.gif

如何?很容易吧! 有沒有體驗到下面的感覺呢?

  1. Flux framework 資料夾結構的分類
  2. React component 可以重構、組裝畫面
  3. JSX 語法方便替換 html tag

這麼多 component, 要怎麼用 Material-UI?

一打開 Component API 有沒有嚇一跳呢?要怎麼把它們組起來? Screen Shot 2018-10-24 at 2.27.53 PM.png

其實,可以不用一個個查,常用的 Demo 都有人整理好了,只要開 Component Demos Screen Shot 2018-10-24 at 2.29.12 PM.png 選你要的畫面 Demo,然後找到 Show the source 按鈕 Screen Shot 2018-10-24 at 2.30.37 PM (2).png 就可以查到原始碼了,再貼到自己的專案中,就可以用了。

每個 Demo 頁面最下面還很貼心的附上相關的 API 連結,就可以進去看更多 component 用法。 Screen Shot 2018-10-24 at 2.35.36 PM.png

樣式(css style)注入系統:「封裝 sytle」 或 「修改/替換樣式」

幾乎所有的 Demo code 最後面都會看到 withStyles 這個 HOC,如下圖: Screen Shot 2018-10-24 at 2.42.28 PM.png 這是那來封裝 sytle 或 overwrite style 用的,它的簽章如下:

1styles => component => component

withStyles(styles) 時就是函數:

1component => component

它把 component (class) 送入,產生新的 component (class)。不僅如此,它還把注入的 styles,放在 classes prop 中,你就可以在 component 使用注入的 style。

因為 withStyles 會把樣式注入 classes prop 中,所以 component 的 prop 定義 (propTypes) 要有 classes: PropTypes.object.isRequired。沒加入的話, Material-UI 也會警告你。

封裝 sytle

注意看到 styles 是巢狀物件,屬性名是 css class name,屬性值是 CSS 物件 (見 CSS Object Model)。 透過 withStyles(styles)(Component) 後,它會把 css class name 也當做 classes prop 的屬性名但是 值是字串,也就是:

1const styles = {
2  myRoot: {
3    backgroundColor: 'red',
4  }
5}

變成

1this.props.classes.myRoot = '某個含有 myRoot 的字串的 css class name' 

舉個使用 withStyles 例子:

 1const styles = {
 2  myRoot: {
 3    backgroundColor: 'red',
 4  }
 5}
 6
 7const Box = (props) => (
 8  <div className={props.classes.myRoot}>  <------ this.props.classes.myRoot 是字串不是 styles.myRoot (CSS 物件)
 9    hi
10  </div>
11);
12
13const withStyleBox = withStyles(styles)(Box);
14
15// uasge: <withStyleBox />

加載多個 style / 加載多個 class name

當你知道事實

1this.props.classes.<style anme> = <string>

你會知道 component 如何加載多個 style:

 1const styles = {
 2  myRoot: {
 3    backgroundColor: 'red',
 4  },
 5  myLayout: {
 6    text: 'center',
 7  }
 8}
 9
10const Box = (props) => (
11  <div className={props.classes.myRoot + ' ' + props.classes.myLayout}>  <------ 就和加載多個 class name 一樣
12    hi
13  </div>
14);
15
16const withStyleBox = withStyles(styles)(Box);
17
18// uasge: <withStyleBox />

若你希望 props.classes.myRoot + ' ' + props.classes.myLayout 是動態的,可以使用 classnames 套件,有條件的控制名稱是否出現,如:

1classNames({ [props.classes.myRoot]: true, [props.classes.myLayout]: true }); // =>  props.classes.myRoot + ' ' + props.classes.myLayout
2classNames({ [props.classes.myRoot]: true, [props.classes.myLayout]: false }); // =>  props.classes.myRoot

覆寫(overwrite) style: 修改/替換樣式

假如我們打開 Button,往下看到 CSS API Screen Shot 2018-10-24 at 3.32.56 PM.png

這就是你可以覆寫原始 component 樣式的 API。

直接舉個官網例子

 1const StyledButton = withStyles({
 2  root: {
 3    background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
 4    borderRadius: 3,
 5    border: 0,
 6    color: 'white',
 7    height: 48,
 8    padding: '0 30px',
 9    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
10  },
11  label: {
12    textTransform: 'capitalize',
13  },
14})(Button);

withStyles 就可以替換 rootlabel 原來的樣式。

最後一個功能是:排版(layout)。

排版 component:回應/響應式網頁(responsive web)

Material-UI 和 bootstrap 一樣,它提供 Responsive API,只不過是 component 版本的。

Grid 就是拿來做 responsive layout。它和 bootstrap 一樣採用 12-column grid layout(12欄網格系統)

不管螢幕大小,一個 row 就是被切成 12 欄,你要設定排版怎麼分配欄位數。直接看例子:

 1<Grid container className={classes.root} spacing={16}>
 2  <Grid item xs={6} sm={4}>
 3    A
 4  </Grid>
 5  <Grid item xs={6} sm={4}>
 6    B
 7  </Grid>
 8  <Grid item xs={6} sm={4}>
 9    C
10  </Grid>
11</Grid>

你就知道怎麼用了,更多例子見 Grid Demos

我們的心力還要花在設定 Breakpoints,每個 breakpoint 有固定的螢幕大小

1xs, extra-small: 0px or larger
2sm, small: 600px or larger
3md, medium: 960px or larger
4lg, large: 1280px or larger
5xl, extra-large: 1920px or larger

以上面例子來說:

  1. xs 指的是當螢幕大於等於 0px 時要採用的「分配欄位數」,所以上面的結果是:
    1A B
    2C
    
    因為 ABGrid 各佔了6欄,它們剛好就是一個 row,所以 CGrid 就會在下一 row。
  2. sm 指的是當螢幕大於等於 600px 時,結果是:
    1A B C
    
    因為 ABC 可以剛好放滿一個 row 共 12 欄。

Hidden component

有時你會需要只有特定的螢幕大小,component 才會要出現/隱藏,這時候可以用 Hidden component

文件中給個例子:

1innerWidth  |xs      sm       md       lg       xl
2            |--------|--------|--------|--------|-------->
3width       |   xs   |   sm   |   md   |   lg   |   xl
4
5smUp        |   show | hide
6mdDown      |                     hide | show
  1. 1<Hidden smUp>
    2  ...may hidden dependent on screen size
    3</Hidden>
    

    你的螢幕大小若大於等於 600px ,Hidden 內的東西就會隱藏。反之,小於 600px 時會顯示。

  2. 1<Hidden mdDown>
    2  ...may hidden dependent on screen size
    3</Hidden>
    

    你的螢幕大小若小於 1280px ,Hidden 內的東西就會隱藏。反之,大於等於 1280px 時會顯示。

總結

今天介紹了 Material-UI 的常用功能

  1. 基本 component 的使用
  2. 使用 withStyles 樣式注入
  3. 使用 Grid 操作12欄網格系統

當沒有人寫做切版時,Material-UI 是一個可選方案。若你想做的畫面很絢麗、酷炫或太過客製化的 component,請花時間在 HTML + CS + Javascript中。若你願意包成 component 提供別人使用(如:React Date Picker),會有人感謝你的,

參考資料