[React Conf 2017] React + ES next = ♥
Video
主要在介紹 ES6+ 以後的東西(投影片):
- Destructuring
- Spread operator
- Arrow functions
- Promises
- Async functions
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
把本來不是 Promise
的 callback
改成 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})
}