pcwu's TIL Notes


[React Conf 2017] React + ES next = ♥

Video

主要在介紹 ES6+ 以後的東西(投影片):

Destructuring

解構賦值,這個滿簡單其實一直都有在用了。

// After
let {comments} = this.state

// Before
let comments = this.state.comments

一般都是直接取相同名字的變數來解構賦值,如果要不同名字的話:

// before
let authorName = this.state.author
let fullText = this.state.text

// after
let { author: authorName, text: fullText } = this.state

Spread operator

... 來代表「其他」:

// after
let newComment = {...comment, id: Date.now()}
let newComments = [...comments, newComment]

// before
let newComment = comment
newComment.id = Date.now()
let newComments = comments.concat([newComment])

製作一個新的 Array:

// [1, 2, 3, 4, 5]
let verbose = new Array(1, ...values, 5)

// [1, 2, 3, 4, 5]
let shorthand = [1, ...values, 5]

當然 Object 也可以:

// after
let warriors = {Steph: 95, Klay: 82, Draymond: 79}
let newWarriors = {
    ...warriors,
    Kevin: 97
}

// before
let warriors = {Steph: 95, Klay: 82, Draymond: 79}
let newWarriors = _.assign({}, warriors, {
    Kevin: 97
})

Arrow functions

毫無反應,就只是箭頭函式:

// ES6
$.ajax({
    url: this.props.url,
    data: comment,
    success: (resJson) => {
        this.setState({comments: resJson})
    }
})

// ES5 WAY
$.ajax({
    url: this.props.url,
    data: comment,
    success: function(resJson) {
        this.setState({comments: resJson})
    }.bind(this) // pass in proper `this` context
})

Promises

Replace callback-style programming with promises

舊時代的 XMLHttpRequest 就不要再用了,現在是 fetch 的時代:

_handleCommentSubmit(comment) {
  // initializations

  fetch(this.props.url, {
    method: 'POST',
    body: JSON.stringify(comment)
  })
    .then((res) => res.json())
    .then((resJson) => {
      this.setState({comments: resJson})
    })
    .catch((ex) => {
      console.error(this.props.url, ex)
    })
}

Promise 分段拆解:

const fetchJson = (path, options) => (
  fetch(`${DOMAIN}${path}`, options)
    .then((res) => res.json())
)
const fetchComments = () => fetchJson('/api/comments')
const getUniqueCommentAuthors = () => (
  fetchComments()
    .then((comments) => comments.map(({author}) => author))
    .then((authors) => _.uniq(authors))
    .catch((ex) => console.error('Something bad happened', ex))
)
getUniqueCommentAuthors()
    .then((uniqueAuthors) => { this.setState({uniqueAuthors}) })
})

或透過 new Promise把本來不是 Promisecallback 改成 Promise

const sleep = (delay = 0) => (
    new Promise((resolve) => {
        setTimeout(resolve, delay)
    })
)

sleep(3000)
  .then(() => getUniqueCommentAuthors())
  .then((uniqueAuthors) => { this.setState({uniqueAuthors}) })
const readFile = (filePath) => (
    new Promise((resolve, reject) => {
        fs.readFile(filePath, (err, data) => {
            if (err) { reject(err) }
            resolve(data)
        })
    })
)

readFile('path/to/file')
  .then((data) => console.log('Here is the data', data))
  .catch((ex) => console.error('Arg!', ex))

當然不能錯過一次做好幾件事,再一起回收結果的 Promise.all()

Promise.all([
    fetchComments(),
    fetchPosts(),
    fetchUsers()
])
  .then((responses) => {
      let [comments, posts, users] = responses

      this.setState({
          comments,
          posts,
          users
      })
  })

Async functions

Use normal control flow with async/await

最近最熱門的 Async / Await

最好要使用 try / catch 控制錯誤:

async _handleCommentSubmit(comment) {
    // initializations
    try {
        let res = await fetch(this.props.url, {
            method: 'POST',
            body: JSON.stringify(comment)
        })
        newComments = await res.json()
    } catch (ex) {
        console.error(this.props.url, ex)
        newComments = comments
    }

    this.setState({comments: newComments})
}

Result

如果使用上述的 ES6+ 技巧,可以將以下的程式碼:

// App.js
_handleCommentSubmit(comment) {
    let comments = this.state.comments;
    let newComment = comment;
    let newComments;

    newComment.id = Date.now();

    newComments = comments.concat([newComment]);
    this.setState({comments: newComments});

    $.ajax({
        url: this.props.url,
        type: 'POST',
        data: comment,
        success: function(resJson) {
            this.setState({comments: resJson});
        }.bind(this),
        error: function(xhr, status, err) {
            this.setState({comments});
            console.error(this.props.url, status, err.toString());
        }.bind(this)
    });
}

改進成:

// App.js
async _handleCommentSubmit(comment) {
    let {comments} = this.state
    let newComment = {...comment, id: Date.now()}
    let newComments = [...comments, newComment]

    this.setState({comments: newComments})

    try {
        let res = await fetch(this.props.url, {
            method: 'POST',
            body: JSON.stringify(comment)
        })
        newComments = await res.json()
    } catch(ex) {
        console.error(this.props.url, ex)
        newComments = comments
    }

    this.setState({comments: newComments})
}

Reference & Reading