React Conf 2017 干貨總結1: React + ES next = ?

React Conf 2017在加利福尼亞州的圣克拉拉萬豪酒店圓滿落幕,這已經是Facebook舉辦的第三屆React官方大會了。
雖然不能參會,但是作為前端開發者,我們當然不能錯過這個絕佳的學習契機。

筆者利用清明假期,參看了YouTube上關于這次大會的記錄。一共34個精彩演講,對應34個視頻。獲益匪淺。

這里,將會作為一個系列,對其中的幾篇演講進行翻譯和分析。并輔助以code demo,幫助大家理解。

歡迎關注我的簡書掘金賬號,也歡迎在Github上follow,最新的大會code demo,便可第一時間掌握。

今天為大家介紹的是Ben Ilegbodu的主題:**React + ES next = ? **

Ben Ilegbodu是“為數不多的”參會有色程序員,黑人程序員如同女性程序員一樣鳳毛麟角。但是,本次分享主題很有愛,很有營養,精彩程度絲毫不打折扣。如果你不了解React也不要緊,因為這次講的是ES6、ES7在React中的應用。所以,其實是對下一代ES的普及和介紹。

本文將以

  • Destructuring、
  • Spread Opetator、
  • Arrow Function、
  • Promises、
  • Async Functions

這幾方面展開。并通過nodeJS實現一個兼具前端和后端的小型“評論/留言 發布閱讀系統”。

建議看這篇文章的同時,結合視頻一起研究:Ben Ilegbodu - React + ES next = ? - React Conf 2017

實現預覽

如圖,我們實現了如下的頁面。這是一個前端+后端的全棧小項目。
當然,樣式是極其簡陋的。作為“粗糙”的程序員,實在懶得在頁面上花時間。

es-next.png

我們可以在輸入框內輸入姓名和留言內容。并點擊按鈕提交。后臺使用nodeJS express框架,實現對文件的讀寫更新。

app.post('/api/comments', function(req, res) {
    fs.readFile(COMMENTS_FILE, function(err, data) {
        if (err) {
            console.error(err);
            process.exit(1);
        }
        var comments = JSON.parse(data);
        var newComment = {
            id: Date.now(),
            author: req.body.author,
            text: req.body.text,
        };
        comments.push(newComment);
        fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) {
            if (err) {
                console.error(err);
                process.exit(1);
            }
            res.json(comments);
        });
    });
});

這是nodeJS的后端代碼,如果不理解也沒關系。

接下來我們繼續回到演講。

解構Destructuring

在我們的項目中,最初版存在這樣的一段代碼:

_handleCommentSubmit(comment) {
    let comments = this.state.comments;
    ...
    // remaining code
}

請務必記住這個_handleCommentSubmit函數,接下來的全文都是圍繞他展開,并進行一步步拓展。
這個函數的邏輯是對新提交的comment進行處理,首先他需要從state中讀取現有的comments數組。
在使用解構賦值的情況下,我們重構為:

_handleCommentSubmit(comment) {
    let {comments} = this.state;
    ...
    // remaining code
}

也許這還看不出來解構到底有什么作用,但是在邏輯多的時候,他是很有必要的。比如:

let author = this.state.author;
let text = this.state.text;

就可以寫為:

let {author, text} = this.state;

如果需要改變變量名時,就可以從:

let authorName = this.state.author;
let fullText = this.state.text;

改為:

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

再舉一個例子,在function component(React無狀態組件編寫的一種推薦形式)情況下:

function MyComponent(props) {
    return (
        <div style={props.style}>{props.children}</div>
    )
}
<MyComponent style="dark">Stateless function!</MyComponent>

我們可以改寫為:

function MyComponent({children, style}) {
    return (
        <div style={style}>{children}</div>
    )
}
<MyComponent style="dark">Stateless function!</MyComponent>

展開符Spread Opetator

還記得上面那個_handleCommentSubmit函數嗎?
接下來我們要進行擴充。首先我們將新提交的comment(即函數參數),添加一個時間戳作為id。接下來,
我們拿到舊的state.comments之后,就要將新的comment(包含id)加入到state.comments當中。

初版做法是:

_handleCommentSubmit(comment) {
    let {comments} = this.state;
    let newComment = comment;
    
    newComment.id = Date.now();

    let newComments = comments.concat([newComment]);

    ...
    // setState + ajax stuffs
}

在使用展開符后,我們可以重構為:

_handleCommentSubmit(comment) {
    let {comments} = this.state;
    let newComment = {...comment, id: Date.now()};
    let newComments = [...comments, newComment];
    ...
    // setState + ajax stuffs
}

當然,展開符還有很多其他benefits;比如,平時我們可以使用Math.max方法對數組求出最大值:

var arrayOfValue = [33, 2, 9];
var maxValueFromArray = Math.max.apply(null, arrayOfValue)

這個思路利用了apply接受一個數組作為函數參數的特性。
使用展開符,我們就可以:

var arrayOfValue = [33, 2, 9];
var maxValueFromArray = Math.max(...arrayOfValue);

同理,我們可以這樣擴充一個數組:

let values = [2, 3, 4];
let verbose = [1, ...values, 5];

當然,以上都是對數組的展開。

對對象屬性的展開符的使用,也已經到了Stage3階段。今后,我們可以這樣寫代碼:

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

而不必再使用Object.assign進行對象的擴展。

箭頭函數Arrow Function

箭頭函數帶來的好處無疑是對this的綁定。
現在再回到我們的_handleCommentSubmit函數。做完了初始化工作,我們需要將新的comment通過ajax,異步發送給后端。在成功的回調函數中,進行setState處理:

$.ajax({
    url: this.props.url,
    data: comment,
    success: function(resJson) {
        this.setState({comments: resJson})
    }.bind(this)
})

有經驗的同學注意到了我使用bind更改this指向的處理。
在ES6箭頭函數下,我們可以直接:

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

我們再展開看看箭頭函數的幾種用法(使用方式):

  • 匿名函數,單參數情況,比如實現一個數組的各項平方:
let squares = [1, 2, 3].map(value => value * value);
  • 匿名函數,多參數情況,比如實現一個數組的各項累加:
let sum = [9, 8, 7].reduce((prev, value) => prev + value, 0)

這時候,需要把參數放在括號之中。

  • 非匿名函數,無返回值情況:
const alertUser = (message) => {
    alert(message)
}

這時候,函數體用花括號圍起來。

  • 更復雜的情況,比如上面我們用到過的:
const MyComponent = ({children, style}) => (
    <div style={style}>{children}</div>
)

Promises

上面的代碼我們使用了jquery的ajax方法發送異步請求,這可能在某些情況下出現“回調地獄”的情況。為此,我們使用基于Promise的fetch方法,進行重構:

_handleCommentSubmit(comment) {
    // ...
    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)
        })
}

當然,我們可以把上述代碼做的更抽象:

const fetchJson = (path, options) => {
    fetch(`${DOMAIN}${path}`, options)
        .then((res)=>res.json())
}

我們在拉取評論時,就可以:

const fetchComments = () => fetchJson('api/comments')

基于promises的fetch,讓異步請求變的靈活可靠。

同時,我再安利一個基于promises的sleep函數

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

sleep(3000)
    .then(()=>getUniqueCommentAuthors())
    .then((uniqueAuthors)=>{this.state({uniqueAuthors})})

回到我們的Project中,在后端nodeJS實現里,我們把所有的評論存在data/comments.json文件當中。
在渲染視圖時,就不可避免地要進行對data/comments.json文件讀取,這個過程也應該是異步完成的:

const readFile = (filePath) => (
    new Promise((resolve, reject)=>{
        fs.readFile(filePath, (err, data)=>{
            if (err) {reject(err)}
            resolve(data)
        })
    })
)

readFile('data/comments.json')
    .then((data)=>console.log('Here is the data', data))
    .catch((ex)=>console.log('Arg!', ex))

Async Functions

當然,Promises實現也有缺點。

更先進的方式,就是使用Async,回到我們的_handleCommentSubmit方法,我們可以重構為:

async _handleCommentSubmit(comment) {
    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});
}

總結

這篇演講生動形象地闡釋了React是怎樣與ES next融合天衣無縫的。不論是React也好,還是ES也好,其實筆者認為說到底,都是為了更大限度地解放生產力。

歡迎讀者與我交流,有任何問題可以留言。今后幾天,將有更多新鮮的react conf視頻翻譯奉獻給大家。

Happy Coding!

PS: 作者Github倉庫,歡迎通過代碼各種形式交流。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容