2017年7月27日 星期四

Redux Thunks Tutorial


  • 通常asynchronous request有三個stage
    • Request開始
    • Request成功 
    • Request失敗

  • 假設有一個reducer其初始state如下
const initialState = {
  data: [],
  isFetching: false,
  error: ''
}

  • 這個Reducer長的如下,我們處理了上面提到的三個stage
function reducer (state = initialState, action) {
  switch (action.type) {
    case 'FETCHING_DATA' :
      return Object.assign({}, state, {
        isFetching: true
      })
    case 'FETCHING_DATA_ERROR' :
      return Object.assign({}, state, {
        isFetching: false,
        error: action.error,
      })
    case 'FETCHING_DATA_SUCCESS' : 
      return Object.assign({}, state, {
        isFetching: false,
        error: '',
        data: action.data
      })
  }
}


  • 他的Action Creator像下面這樣
function fetchingData () {
  return {
    type: 'FETCHING_DATA'
  }
}
function fetchingDataError (error) {
  return {
    type: 'FETCHING_DATA_ERROR',
    error: error.msg
  }
}
function fetchingDataSuccess (data) {
  return {
    type: 'FETCHING_DATA_SUCCESS',
    data,
  }
}

  • 接下來考慮一個常見的情況,我們可能在Component要mount的時候去發出asynchronous request,假設getData( )是一個asynchronous function會回傳一個Promise,他會以如下的方式跟Action Creator整合
componentDidMount () {
  fetchingData()
  getData()
    .then((data) => fetchingDataSuccess(data))
    .catch((error) => fetchingDataError(error))
}

  • 上面那樣看起來不錯,只是要把那些Action Creator都import到我們的Component有點麻煩,而且還得用bindActionCreators把他們跟dispatch bind起來,以及做PropTypes的檢查等。是不是把這些邏輯封裝到另一個Action Creator會更好呢?假設有一個Action Creator叫做fetchAndHandleData可以做到這些,那麼componentDidMount只要像這樣就好:
componentDidMount () {
  fetchAndHandleData()
}

  • 只是要做到這樣的話,就必須讓那個Action Creator得以存取dispatch,這就是redux-thunk出現的原因,他的想法是Action Creator不是回傳一個物件,而是回傳一個傳入dispatch的function,以上面的fetchAndHandleData為例大概會長的像這樣:
function fetchAndHandleData () {
  return function (dispatch) {
      dispatch(fetchingData())
      getData()
          .then((data) => dispatch(fetchingDataSuccess(data)))
          .catch((error) => dispatch(fetchingDataError(error)))
  }
}


  • 最後是實作的時候要import的東西,以及在createStore( )時多帶入一個參數
...
import thunk from 'redux-thunk'
import { createStore, applyMiddleware } from 'redux'
...
const store = createStore(users, applyMiddleware(thunk))
...





2017年7月20日 星期四

Connecting our First Component with Redux and React

Redux Tutorial: createStore


  • 我們來看看一個基本的reducer,假設這個reducer第一次被呼叫,那state會是怎麼樣的? 由於default state是一個物件,所以它會是undefined。
function reducer (state, action) {
  switch (action.type) {
    case 'SUBMIT_USER' :
      return Object.assign({}, state, {
        name: action.user
      })
    case 'CHANGE_LOADING' :
      return Object.assign({}, state, {
        isLoading: action.loading
      })
    default : 
      return state
  }
}


  • 因此我們需要給reducer一個initial state,當第一次呼叫是undefined的時候,state就會設置成initial state。
const initialState = {
  name: '',
  isLoading: false
}
function reducer (state = initialState, action) {
  switch (action.type) {
    case 'SUBMIT_USER' :
      return Object.assign({}, state, {
        name: action.user
      })
    case 'CHANGE_LOADING' :
      return Object.assign({}, state, {
        isLoading: action.loading
      })
    default : 
      return state
  }
}


  • 我們的application最後會有好幾個reducer,所以application的state會是一個物件,他結合了每一個reducer的initial state。
  • 現在我們知道用設置reducer的initial state的方式可以初始化產生single state tree,但實際上要怎麼做呢? Redux提供一個createStore( ) method,可以傳一個reducer或是一個包含多個reducer的物件給它,它會以undefined state為參數去呼叫每個reducer,以此來初始化各個state並且把state return回來。
import { createStore } from 'redux'
import myReducer from '../reducer'
const store = createStore(myReducer)


  • 我們如何將single state tree整到React呢? 下面是一般初始化React component的作法,假設用React Router:
ReactDOM.render(
  {routes},
  document.getElementById('app')
)


  • 我們可以使用"react-redux" module提供的Provider,改去render這個Provider,並且把store帶進去property。
import { Provider } from 'react-redux'
import myReducer from '../reducer'
.
.
.
const store = createStore(myReducer)
ReactDOM.render(
  <Provider store={store}>
    {routes}
  </Provider>,
  document.getElementById('app')
)

Connect: dispatch, mapStateToProps, mapDispatchToProps, bindActionCreators

  • 之前我們有提過需要dispatch用以呼叫Reducer並且可以把Action帶進去。使用react-redux,dispatch會當成prop傳入component,為達此目的我們必須讓component跟Redux連結起來,你可以使用react-redux的connect( ) method來做到。
  • 下面是一個範例,不是將component export出去,而是將connect function的呼叫export出去,它會return一個function,接著會將component帶到那個function中。這樣就可以產生所謂的Container Component。範例中有一個dispatch function被當成prop傳入component,當你點擊div,Reducer就會被呼叫,state也會跟著改變。
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { setUser } from '../actions'
const MyComponent = React.createClass({
  propTypes: {
    dispatch: PropTypes.func.isRequired,
  },
  handleClick () {
    this.props.dispatch(setUser('Tyler'))
  },
  render () {
    return (
      <div onClick={this.handleClick}> Hello </div>
    )
  }
})
export default connect()(MyComponent)


  • 將你的application分成Container及Presentational Components是不錯的主意
    • Container Component
      • 處理邏輯且實際跟Redux連結
    • Presentational Component
      • 接收props且render UI
  • 接下來的問題是,若有component需要state tree的部分資訊怎麼辦? 假設一個state tree長的如下:
{
  authedId: 'tyler',
  name: 'Tyler McGinnis'
  isAuthed: true,
  isFetching: false,
  bio: 'Posuere ad repellendus odit sagittis? Non velit do mollitia dignissim, quam nihil cupidatat laboriosam'
}

  • 接著假設有一個basic component需要render prop: name及bio
const MyComponent = React.createClass({
  propTypes: {
    name: PropTypes.string.isRequired,
    bio: PropTypes.string.isRequired,
  },
  render () {
    return (
      <div>
        <h1>{this.props.name}</h1>
        <p>{this.props.bio}</p>
      </div>
    )
  }
})

  • 我們可以這樣做,定義一個mapStateToProps( ) function會回傳從帶入的state中得到的資訊而產生的物件,並將mapStateToProps當成connect( )的第一個參數傳入,這樣就可以告訴Redux那些prop我要傳到component中。
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
const MyComponent = React.createClass({
  propTypes: {
    name: PropTypes.string.isRequired,
    bio: PropTypes.string.isRequired,
  },
  render () {
    return (
      <div>
        <h1>{this.props.name}</h1>
        <p>{this.props.bio}</p>
      </div>
    )
  }
})
function mapStateToProps (state) {
  return {
    name: state.name,
    bio: state.bio
  }
}
export default connect(mapStateToProps)(MyComponent)


  • 接下來討論幾個方便但非必須的method。
  • 假設我們有多個action想要去dispatch,長的可能如下,這段程式碼是work的,但有多個 this.props.dispatch(actionCreator())有點麻煩,而為了包裝setUser()的dispatch還得特地定義handleClear(),為什麼不直接把Action跟dispatch bind在一起就好呢!
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { setUser, addPost, fanoutPost } from '../actions'
const MyComponent = React.createClass({
  propTypes: {
    dispatch: PropTypes.func.isRequired,
    name: PropTypes.string.isRequired,
  },
  handleClear (user) {
    this.props.dispatch(setUser(user))
  },
  submit (post) {
    this.props.dispatch(addPost(post))
    this.props.dispatch(fanoutPost(post))
  },
  render () {
    return <Post name={this.props.name} handleSubmit={this.submit} handleClear={this.handleClear} />
  }
})
function mapStateToProps (state) {
   return {
     name: state.name
   }
}
export default connect(mapStateToProps)(MyComponent)

  • connect( ) function的第二個參數可以允許我們將Action跟dispatch bind在一起。我們定義mapDispatchToProps ( ) function,他應該要回傳一個物件,物件裡面的method會被當成prop傳入component。bindActionCreators( )會把所帶入的Action Creators一個個被dispatch包裝起來,並將包裝後的結果當成prop放在物件中並return回去。
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as actions from from '../actions'
const MyComponent = React.createClass({
  propTypes: {
    name: PropTypes.string.isRequired,
    setUser: PropTypes.func.isRequired,
    addPost: PropTypes.func.isRequired,
    fanoutPost: PropTypes.func.isRequired,
  },
  submit (post) {
    addPost(post)
    fanoutPost(post)
  },
  render () {
    return <Post name={this.props.name} handleSubmit={this.submit} handleClear={this.props.setUser} />
  }
})
function mapStateToProps (state) {
   return {
     name: state.name
   }
}
function mapDispatchToProps (dispatch) {
  return bindActionCreators(actions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)











2017年7月18日 星期二

CSS Modules with React


  • 每個React Component可以有自己的.css檔,透過webpack就可以讓它像JavaScript檔一樣被import進來,
import styles from 'styles.css'
function Button () {
  return <div className={styles.container}>Yo</div>
}


  • 這樣做也可以更簡單地讓css class互相做組合(compose)。不僅在同一個檔案裡面可以組合不同的class,甚至也可以組合不同檔案裡面的class。
/* colors.css */
.primary {
  background-color: #ff000;
}
.button {
  height: 50px;
  width: 100px;
}
.dark {
  composes: button;
  background: #000;
}
.light {
  composes: button;
  composes: primary from '../shared/colors.css';
}







2017年7月13日 星期四

Redux Introduction


  • 用一個去銀行存款的例子來說明Redux的概念
  • 對銀行來說,每個存戶的帳戶資訊可以記錄在一個state裡面如下,裡面存的資料是key-value的形式,key是該帳戶的號碼,value是帳戶資訊
const state = {
  6685943: {
    name: 'Tyler',
    balance: 20
  },
  6685266: {
    name: 'Mikenzi',
    balance: 60
  },
  668508: {
    name: 'Jake',
    balance: 50
  }
}


  • 想要存錢的話必須填存款單,你必須寫清楚要存到哪個帳戶,存多少錢的資訊。而存款單就等同於Redux中的Action概念Action用來描述我想要對state做怎樣的修改。Action是一個物件,其中type欄位是必須的,搭配其他欄位資訊可以描述想要做的動作。
const action = {
  type: 'DEPOSIT',
  accountNumber: 6685943
  amount: 10
}


  • 接著你會把存款單跟錢放到一個透明的管子裡面,它不是必須的,但它可以讓你更方便的把存款單跟錢交給行員。這根管子在Redux裡面是Action Creator的概念。Action Creator讓你的action是portable且易於測試,通常會把它包成一個function,創造並return一個Action。
function handleDeposit (accountNumber, amount) {
  type: 'DEPOSIT',
  accountNumber,
  amount
}


  • 行員負責將管子內的東西取出,根據上面的資訊決定怎麼處理你的存款。行員在Redux裡面是Reducer的概念Reducer會接受目前的state以及要做的action,產生目前state的一份copy,根據action修改這個copy,最後把這個copy設定成state
function reducer (state, action) {
  switch (action.type) {
    case 'DEPOSIT' :
      return Object.assign({}, state, {
        [action.accountNumber]: state.amount + action.amount
      })
    case 'WITHDRAWAL' :
      return Object.assign({}, state, {
        [action.accountNumber]: state.amount - action.amount
      })
    default :
      return state
  }
}


  • 那要怎麼呼叫Reducer並且帶給它特定的action呢? Redux提供一個dispatch( ) function,讓你可以帶入action (通常由Action Creator產生),接著Reducer就會被呼叫,並且將現在的state以及所帶入的action帶進去Reducer。下面的例子中,submitUser()是一個Action Creator。
dispatch(submitUser('Tyler'))








Redux Schema

Normalzing your Data Store for Redux

  • Firebase及Redux對資料結構的準則是相反的
  • Firebase鼓勵重複的資料,因為沒有table或是key可以reference到其他地方,所以只能透過重複的資料或是多次的request來取得資料
  • Redux (也就是在JavaScript中)則可以透過id的array來reference到想要的物件以避免重複的資料出現。下面是一個例子:
const people = {
  kassidi: {
    name: 'Kassidi Henry',
    age: 24,
    favoriteMovie: 'Remember the Titans'
  },
  tyler: {
    name: 'Tyler McGinnis',
    age: 25,
    favoriteMovie: 'Fatigue: A JavaScript Story'
  },
  jake: {
    name: 'Jake Lingwall',
    age: 26,
    favoriteMovie: 'Casablanca'
  },
}
const friends = ['kassidi', 'jake']


  • 若我想要建立一個新的物件以列出所reference到的朋友資訊,就可以這樣存取:
friends.map((friend) => people[friend])




2017年7月12日 星期三

Firebase Schema

Denormalizing your Data Store for Firebase


  • 在Firebase上存的資料是jason的文件
  • 資料項目是以key-value的格式表示,value的值可以是數字/字串/其他物件,但是不支援array。
  • 當要建構Firebase資料庫時,記住下列兩個規則:
    • 讓資料的階層淺一點(shallow)
    • 不要怕重複的資料

  • Firebase上的資料是以URL的方式來存取
  • 若我建了一個主要的project叫`Uber for Dogs`,他就會存在於此網址:https://uber-for-dogs.firebaseio.com/
  • 若我存了一個物件如下
{
  name: 'Anthony'
}


  • 那麼我就可以在 https://uber-for-dogs.firebaseio.com/name取得Anthony這個字串
  • 假設資料更複雜一點如下,想要查詢某個user時只要去/users/some-user-id查詢還蠻方便。但若是想查詢某篇文章,而且資料內有很多個user,那就必須要查過所有user才能找到該篇文章。
users: {
    tylermcginis: {
        name: 'Tyler McGinnis',
        age: 25,
        posts: {
            post1: {
                title: 'What is a jQuery',
                date: 1461131626103,
                likes: {
                    'jakelingwall': true,
                    'eanplatter': true,
                    'mikenzi': true
                },
                replies: {
                    reply1: {
                        name: 'Jake Lingwall',
                        comment: 'Its a monad',
                        id: 'jakelingwall'
                    },
                    reply2: {
                        name: 'Ean Platter',
                        comment: 'Its a type of Java',
                        id: 'eanplatter'
                    }
                }
            }
        }
    }
}


  • 你可以將資料改成下列格式,讓重複的資料來加快尋找特定文章的速度,但重複的資料又會有資料是否能sync的困難點
users: {
    tylermcginis: {
        name: 'Tyler McGinnis',
        age: 25,
        posts: {
            post1: {
                title: 'What is a jQuery',
                date: 1461131626103,
                likes: {
                    'jakelingwall': true,
                    'eanplatter': true,
                    'mikenzi': true
                },
                replies: {
                    reply1: {
                        name: 'Jake Lingwall',
                        comment: 'Its a monad',
                        id: 'jakelingwall'
                    },
                    reply2: {
                        name: 'Ean Platter',
                        comment: 'Its a type of Java',
                        id: 'eanplatter'
                    }
                }
            }
        }
    }
},
posts: {
    post1:{
        title: 'What is a jQuery',
        date: 1461131626103,
        likes: {
            'jakelingwall': true,
            'eanplatter': true,
            'mikenzi': true
        },
        replies: {
            reply1: {
                name: 'Jake Lingwall',
                comment: 'Its a monad',
                id: 'jakelingwall'
            },
            reply2: {
                name: 'Ean Platter',
                comment: 'Its a type of Java',
                id: 'eanplatter'
            }
        }
    }
}


  • 下面的資料格式則是讓資料階層變很淺,並且把重複的資料移除,但要查詢某篇文章時,必須送出三個request (posts/replies/likes),所以這個方式也不是最好的
users: {
    tylermcginis: {
        name: 'Tyler McGinnis',
        age: 25,
        posts: {
            post1: true,
        }
    }
},
posts: {
    post1:{
        title: 'What is a jQuery',
        date: 1461131626103,
    }
},
replies: {
    post1: {
        reply1: {
            name: 'Jake Lingwall',
            comment: 'Its a monad',
            id: 'jakelingwall'
        },
        reply2: {
            name: 'Ean Platter',
            comment: 'Its a type of Java',
            id: 'eanplatter'
        }
    }
},
likes: {
    post1: {
        'jakelingwall': true,
        'eanplatter': true,
        'mikenzi': true
    }
}


  • 最後列出一個折衷方案
users: {
    tylermcginis: {
        name: 'Tyler McGinnis',
        age: 25,
        posts: {
            post1: true,
        }
    }
},
posts: {
    post1:{
        title: 'What is a jQuery',
        date: 1461131626103,
        likes: {
            'jakelingwall': true,
            'eanplatter': true,
            'mikenzi': true
        },
        replies: {
            post1: {
                reply1: {
                    name: 'Jake Lingwall',
                    comment: 'Its a monad',
                    id: 'jakelingwall'
                },
                reply2: {
                    name: 'Ean Platter',
                    comment: 'Its a type of Java',
                    id: 'eanplatter'
                }
            }
        },
    }
}


  • 這些trade-off的取捨,取決於你的AP的需要,根據你的需要來建構最佳的格式





2017年7月10日 星期一

Async/Await in React

  • Async/Await讓你可以寫出看起來是synchronous但實際上是asynchronous的程式碼
  • 每個async function會回傳Promise
  • 每個所await的東西將會是Promise
  • 這個asynchronous的getUser()回傳一個要花2秒的Promise,這代表我們無法立即取得user物件,而是要等待2秒。
function getUser () {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({name: 'Tyler'}), 2000)
  })
}


  • 為了要處理這樣的delay,可以傳一個function給Promise的`then( )`,這個function會在Promise resolve時(也就是user物件available時)被呼叫
function handleGetUser () {
  getUser()
    .then((user) => {
      console.log('The user is: ', user)
    })
}




  • 當Promise chain丟出error時我們可以去catch它
function handleGetUser () {  
  getUser()
    .then((user) => {
      console.log('The user is: ', user)
    })
    .catch((error) => {
      console.warn('Oh no there was an error: ', error)
    })
}



  • 然而如果把上面的code改成synchronous的樣式如下,則無法得到我們想要的結果。user變數只會拿到一個Promise,印出的log也就只是這個Promise。
function handleGetUser () {
  var user = getUser()
  console.log(user)
}


  • 我們可以做點小修改如下,告訴compiler getUser( )是一個asynchronous的function,那麼他就會暫停執行,一直到resolve完成為止,然後才會印出我們真正要的值
function handleGetUser () {  
  var user = await getUser()
  console.log(user)
}


  • 不幸的是,上面的code還是不work,因為你不能沒有用async就用了await,所以請記住,如果我要用await,就要確保我所await的function位於async的function中
async function handleGetUser () {
  var user = await getUser()
  console.log(user)
}


  • 至於catch error的部分,直接用JavaScript原本就有的try-catch機制即可
async function handleGetUser () {
  try {
    var user = await getUser()
    console.log(user)
  } catch (error) {
    console.log('Error in handleGetUser', error)
  }
}


2017年7月7日 星期五

Understand promises

Reference: 參考資料1 參考資料2

Promise是什麼?


  • Promise是一種特別的JavaScript object,它又包含了其他objects。
  • 使用`.then()`來存取promise中的資料
function getFirstUser() {
    return getUsers().then(function(users) {
        return users[0].name;
    });
}


  • 使用`.catch()`來catch error
function getFirstUser() {
    return getUsers().then(function(users) {
        return users[0].name;
    }).catch(function(err) {
        return {
          name: 'default user'
        };
    });
}


  • Promise是asynchronous的,不論現在就已經有資料或是將來才會有資料回傳。
  • 在上面的code中getUsers()回傳一個Promise。使用ES2016,任何Promise都可以await。await等同於對Promise呼叫`.then( )`而且不需要callback。程式可以改寫如下:
async function getFirstUser() {
    let users = await getUsers();
    return users[0].name;
}


  • await會讓method暫停執行,直到Promise的值是available。
  • 而catch error的方式也可改用synchronous型式的try/catch:
async function getFirstUser() {
    try {
        let users = await getUsers();
        return users[0].name;
    } catch (err) {
        return {
            name: 'default user'
        };
    }
}

Awaiting multiple values


  • 一般的使用下,一次只能await一個東西,下面這段code會依序執行await取得foo跟bar
let foo = await getFoo();
let bar = await getBar();


  • 若我們想讓foo跟bar同時被設置,則可用下列code:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);


  • Promise.all( )是把一個array的Promises組合成單一的Promise,且array中的所有child promise都resolve後才算是真的resolve。
  • Promise.all( )並沒有dispatch你帶進去的Promise,當[getFoo(), getBar()]執行時就已經在做dispatch的動作了。
  • 還有另外一個有趣的方法可以讓async/await可以parallel執行如下:
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;


  • 這段程式碼需要你對Promise有所理解:
    1. 首先dispatch `getFoo`及`getBar`並將其回傳的Promise存在fooPromise及barPromise
    2. 這些動作正在進行中,沒有stop或delay發生
    3. 接著去await各個Promise
    4. 由於在await前就把所有async action都dispatch了,所以它們會同時執行
    5. foo跟bar

Handle callbacks


  • 當你想在Promise的值available時呼叫callback,可用下列方法:
function getFirstUser(callback) {
    return getUsers().then(function(user) {
        return callback(null, users[0].name);
    }).catch(function(err) {
        return callback(err);
    });
}


  • 若在async function裡想要呼叫callback,則須把callback轉成會回傳Promise的function:
function callbackToPromise(method, ...args) {
    return new Promise(function(resolve, reject) {
        return method(...args, function(err, result) {
            return err ? reject(err) : resolve(result);
        });
    });
}
async function getFirstUser() {
    let users = await callbackToPromise(getUsers);
    return users[0].name;
}



  • 記得只要用到sync function就要去catch error做處理


Using side effects instead of returning


  • 在Promise的then( )或catch( )能做三件事
  1. 回傳另一個Promise
    • 這是一個常見的pattern用來組合Promise
getUserByName('nolan').then(function (user) {
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // I got a user account!
});


    •    注意:那個return是重要的!若沒有加上return,則第二個Promise的function會收到undefined而非userAccount。
  1. 回傳一個synchronous的值

    • 第二個Promise的 function並不在意參數是 sync或是async地取得
getUserByName('nolan').then(function (user) {
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];    // returning a synchronous value!
  }
  return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
});


  1. Throw synchronous error 
getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];       // returning a synchronous value!
  }
  return getUserAccountById(user.id);    // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
});

Promises fall through


  • 你以為下列的code會印出bar但實際上會印出boo,這是因為若then( )裡面帶的不是function,則會被視為then(null),前一個Promise的結果會往下傳。
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
  console.log(result);
});





2017年7月6日 星期四

Template Strings. Default Parameters. Concise Objects

Template Strings

  • String的串接以往都是用 '+' 來連接

function makeGreeting (name, email, id) {
  return 'Hello, ' + name + '. We\'ve emailed you at ' + email '. Your user id is ' + id + '.'
}


  • 可以改用如下的Template String就可以不用寫 '+',而且(\')也可以拿掉escape character直接寫('),但括起字串的就不是( ' )而是( ` ),而且變數要用(${})包起來。
function makeGreeting (name, email, id) {
  return `Hello, ${name}. We've emailed you at ${email}. Your user id is ${id}.`
}

Default Parameters

  • 在function中當某個參數未定義時可能會想給一個預設值,以往會用下列寫法
function debounce (func, wait, immediate) {
  if (typeof wait === 'undefined') {
    wait = 1000
  }
  ...
}


  • 但用Default Parameters的話可以更簡潔
function debounce (func, wait = 1000, immediate) {
 ...
}

Concise Objects


  • 下面例子的function回傳一個物件
function getUser (username) {
  const email = getEmail(username)
  return {
    username: username,
    email: email
  }
}


  • 上面那種名稱相同的情況可以用concise object簡化
function getUser (username) {
  const email = getEmail(username)
  return {
    username,
    email
  }
}

Concise Object Methods. Arrow Functions

Concise Object Methods

  • 讓你可以把":function"的部分移除
  • 原本:
var Register = React.createClass({
  getDefaultProps: function () {
  },
  componentDidMount: function () {
  },
  shouldComponentUpdate: function () {
  },
  render: function () {
  }
})


  • 修改後變得比較簡潔
var Register = React.createClass({
  getDefaultProps () {
  },
  componentDidMount () {
  },
  shouldComponentUpdate () {
  },
  render () {
  }
})

Arrow Functions


  • 除了讓code比較簡潔外,還有一個很大的優點是可以共享parent scope的this
  • 下面這個例子會有一個問題,在map內的function中,this.onAddFriend會無法辨別
var FriendsList = React.createClass({
  ...
  onAddFriend (friend) {
    this.setState({
      friends: this.state.friends.concat([friend])
    })
  }
  render () {
    return (
      <ul>
        {this.state.friends.map(function (friend) {
          return <FriendItem key={friend.id} handleAddFriend={this.onAddFriend}>{friend.name}</FriendItem>
        })} 
      <ul/>
    )
  }
});


  • 除了可以在map後加上.bind(this)解決外,還可以將function改成arrow function來解決,因為arrow function部會生成新的context,所以map內外的this是相同的。
  render () {
    return (
      <ul>
        {this.state.friends.map((friend) => {
          return <FriendItem key={friend.id} handleAddFriend={this.onAddFriend}>{friend.name}></FriendItem>
        })}
      </ul>
    )
  }


  • 此外,arrow function會假設去return arrow後面的東西,所以就不需要加上return敘述。
 render () {
    return (
      <ul>
        {this.state.friends.map((friend) => <FriendItem key={friend.id} handleAddFriend={this.onAddFriend}>{friend.name}</FriendItem>)}
      </ul>
    )
  }



如何在Blogger插入程式碼

Step 1:版面配置 => 新增小工具 => HTML/JavaScript

Step 2:在裡面插入下列程式碼
<style>
.post .codeblock {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 10pt;
overflow:auto;
background: #f0f0f0 url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAASwCAYAAAAt7rCDAAAABHNCSVQICAgIfAhkiAAAAQJJREFUeJzt0kEKhDAMBdA4zFmbM+W0upqFOhXrDILwsimFR5pfMrXW5jhZr7PwRlxVX8//jNHrGhExjXzdu9c5IiIz+7iqVmB7Hwp4OMa2nhhwN/PRGEMBh3Zjt6KfpzPztxW9MSAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzB8HS+J9kUTvzEDMwAAAABJRU5ErkJggg==) left top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:1000px;
line-height: 1.2em;
}
</style>

Step 3: 用HTML編輯文件並插入下列程式碼
<pre class="codeblock">
中間可以插入你想放入的程式碼
</pre>


Step 4: 當程式碼包含"< >"時須轉碼
               可至此連結做轉碼動作

另外,若想要自動對程式碼上色,可以再新增一個小工具並插入下列程式碼
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>

就可用下列程式碼達成自動上色的效果
<pre class="codeblock prettyprint">
中間可以插入你想放入的程式碼
</pre>

文章參考連結: 此連結

const. let. Modules. Destructuring


let and const

  • let: 一般常用的var是function scope的,而 let是block scoped,也就是在{..}內可見。
  • const: 和let一樣的scope,並且不能將reference改指到其他物件,但可以修改所reference物件的內容。
  • 老師建議使用的優先權:const > let > var

import and export with ES6 Modules

  • 你可以export一個以上的item
  • 你可以只import部分item而非全部

// math.js
export function add (x,y) {
  return x + y
}
export function multiply (x,y) {
  return x * y
}
export function divide (x,y) {
  return x / y
}
// main.js
import { add, multiply } from './math'
add(1,2) // 3
multiply(3,4) // 12


  • 你也可以用import * as X將該module所有的東西存在一個變數中
// math.js (same as above)
// main.js
import * as math from './math'
math.add(1,2) // 3
math.multiply(3,4) // 12
math.divide(4,4) // 1

  • 你也可以只export一個default item
// math.js
export default function doAllTheMath (x,y,z) {
  return x + y + x * x * y * z / x / y / z
}
// main.js
import doAllTheMath from './math'
doAllTheMath(1,2,3) // 4

  • 你甚至可以混合搭配使用
// math.js
export function add (x,y) {
  return x + y
}
export default function doAllTheMath (x,y,z) {
  return x + y + x * x * y * z / x / y / z
}
// main.js
import doAllTheMath, { add } from './math'
doAllTheMath(1,2,3) // 4
add(1,2) // 3

Object Destructuring


  • Destructuring可以從object跟array中取出data
  • 以下例子中,將props的各項目存到各個變數裡面
function register (props) {
  var onChangeEmail = props.onChangeEmail;
  var email = props.email;
  var onChangePassword = props.onChangePassword;
  var password = props.password;
  var submit = props.submit;
...


  • 改用Destructuring將props內的資料存到與property同名的變數變得比較簡潔
function register (props) {
  var { onChangeEmail, email, onChangePassword, password, submit }  = props;
...


  • 甚至可以直接將{...}替代props的參數
function register ({ onChangeEmail, email, onChangePassword, password, submit }) {
...