翻譯|Thinking Statefully

phpsmarter發表於2019-04-27

‌原文:Thinking Statefully

熟練掌握React的過程包含了如何解決特定問題的方法轉變.這提醒我,這個學習過程有點像在路上不同的方向開車. 第一次感受這一點是,我真正訪問土耳其和卡斯柯島,他們的車都靠左行駛.在美國我們是靠右行駛,所以要花很大的力氣重程式設計. 一出機場就差點完蛋了.

有意思的是,即使我已經可以在正常的直道上行駛,但是在不同情況發生時,大腦仍然把他轉換成了舊的習慣.

轉進停車場?習慣接管了,我開進了錯誤的車道.在停車標誌車左轉?還是同樣的問題.向右轉?你認為我已經瞭解了一切,但是對於我的大腦,完全沒這回事.

我講這個故事是因為在學習React的時候,我的經歷是類似的.我想其他人也有這樣的經歷.

傳遞props到元件(最好把元件看成是函式),大腦也已習慣了. 看起來和HTML的工作差不多.

在簡單例項中,往下傳遞資料,向上傳遞事件也很好理解.這就是回掉模式,在其他的地方也很常見.向按鈕元件傳遞一個onClick handler是相當普通.

但是如果需要Modal對話方塊.或者是提示Badge,要怎麼操作?亦或者根據事件響應來對一個Icon進行動畫操作?可以看到,這些都是命令式的,"事件驅動"的內容在宣告式和狀態時的React世界顯得很不自然.

怎麼開發"狀態式"或者是"宣告式"的思考

如果你有jQuery,Angular的經驗或者其他框架的以經驗,需要呼叫函式執行任務("程式式程式設計"),在React的領域裡為了更為有效的工作就需要調整思維模型.從實踐中可以很快的完成這個轉變-只需要給大腦一些新的例項或者"模式".

這裡是一些例項

開啟/關閉一個Accordion元件(手風琴元件)

舊有辦法: 點選toggle按鈕呼叫toggle函式,從而開啟或者關閉accordion.Accordion元件知道該怎麼關閉或開啟. state方法: Accordion要麼是"open"狀態,要麼是"close"狀態. 這個資訊我們作為一個標記(flag)儲存在Accordion元件的父元件state上(不在Accordion上儲存).以名為isOpen的prop形式傳遞state資訊,Accordion依據prop來進行渲染. 當isOpentrue時,渲染為開啟,反之就關閉.

<Accordion isOpen={true}/>
// or
<Accordion isOpen={false}/>
複製程式碼

這個例項相對簡單.希望不要有什麼認知負擔. 最大的挑戰是以宣告式的React方法處理,開啟/關閉的state是儲存在Accordion元件之外,並以prop的形式傳遞的.

開啟,關閉一個Dialog

‌舊有辦法: 點選按鈕開啟modal,點選按鈕關閉. state方法: Modal是開還是閉是一個狀態,"open","state"兩者居其一.所以state是"open",我們就渲染元件.如果是"close",就不做渲染.此外,可以給Modal傳遞onClose回撥函式-通過這種方法,Modal元件的父元件決定了使用者點選按鈕時應該幹什麼

{this.state.isModalOpen && <Modal onClose={this.handleClose}/>}
複製程式碼

詳細內容請看Modal Dialogs in React

提醒元件

‌舊有辦法: 當一個事件發生時(例如一個錯誤),呼叫提示庫的元件來顯示popup,例如toast.error("Oh,no!")

state方法: 把提示作為一個state.有0個或者1個,或多個提示,儲存在訊息陣列中. 在根元件處安置提醒元件. 傳遞state用於顯示.你可以用不同的方式來管理訊息陣列:

  • 如果使用Redux,儲存在store中,Dispatch actions來新增訊息
  • 如果不使用Redux,可以儲存在根元件的state中,或者是一個單例物件(整個應用中只例項化一次的物件)中.然後就可以傳遞向元件傳遞addNotificationprop,或者匯入可以新增到全域性單例物件的函式.

在應用中,你可以使用軟體包來完成任務,例如react-redux-toastr.但是概念很簡單,所以可以根據你的需求自己編寫.

用動畫顯示改變

假設你有一個有數字的badge用於顯示登入的使用者,元件從prop獲取這個數字.但是如果你想在元件數字變化時有點動效,怎麼處理?

‌老辦法: 可以使用jQuery來toggle(切換)一個執行動畫的類([^譯註:CSS的類]),或者直接用jQuery操控要執行動畫的元素. state方法: 你可以在props改變時通過componentWillReceiveProps宣告周期函式比較新舊值來響應變化.如果改變了,可以把"animating" state設定為true.接著執行render,當"animating"為trur時,新增一個執行動畫的CSS類.為false時,不新增類.這裡是程式碼:

componentWillReceiveProps(nextProps) {
if(this.props.counter !== nextProps.counter) {
  // Set animating to true right now. When that state change finishes,
  // set a timer to set animating false 200ms later.
  this.setState({ animating: true }, () => {
    setTimeout(() => {
      this.setState({ animating: false });
    }, 200);
  });
}
}

render() {
const animatingClass = this.state.animating ? 'animating' : '';
return (
  <div className={`badge ${animatingClass}`}>
    {this.props.counter}
  </div>
);
}
複製程式碼

完成

相關文章