翻譯|Redux Saga:hello单寂,world!

12 OCTOBER 2016

這是翻譯版本,原文請見


第一部分譯文請見
第二部分譯文請見吐辙,
第三部分譯文請見.


簡單的Redux Saga 模板

在這個文章中,我們將完成完整的React/Redux/Redux Saga app,并且來看看為什么要這樣做.

我已經(jīng)創(chuàng)建了一個app的模板作為本文的起點,我們沒有必要關(guān)注一些開發(fā)的細(xì)節(jié),因為這些細(xì)節(jié)不是本系列文章的重點(我假設(shè)你已經(jīng)了解React,Redux以及與此相關(guān)的開發(fā)工具.)但是我仍然會簡單強調(diào)一些內(nèi)容,以便于你對項目依賴包和配置有一些基礎(chǔ)的了解.你可能是個高手,或者是個不折不扣的菜鳥(是菜鳥也沒有關(guān)系)如果你不關(guān)心這些基礎(chǔ)內(nèi)容,直接跳到那副圖片,看看后面的內(nèi)容.

第一步,克隆repo,并且安裝依賴包:

  //原文的repo不能運行了,下面的repo是驗證過的
  git clone https://github.com/granmoe/redux-saga-clock-tutorial.git  
cd redux-saga-clock-tutorial  
npm i  

好了做完上面的工作,使用你喜歡的編輯器打開項目,讓我們先看看里面有些什么內(nèi)容.
在我們的package.json文件中每個元素都是非常標(biāo)準(zhǔn)的,但是要注意,如果要對付不支持ES2015標(biāo)磚的瀏覽器,需要引入babel-polyfill包.這個包必須在redux-saga之前引入(譯者:redux-saga使用了ES2015的技術(shù)宣决,所以要先獲得支持才可以).

你也可以注意到,在package.json中有ESLint依賴包,因為我發(fā)現(xiàn)這個依賴包是開發(fā)中的無價之寶.

下面是我們的babel配置,在.babelrc文件中:

  {
  "presets": ["es2015", "react", "stage-2"]
  }

我已經(jīng)決定使用es2015,react和stage-2.

我還想講講.eslintrc文件,但是我實在是不想讓你看到想睡覺.

webpack和index.html文件沒講,但是這里估計沒有人會對這兩個文件感興趣.

開始進(jìn)入正題吧

patrick Stewart McKellan Elmo.jpg

app的入口文件是main.jsx:

  import 'babel-polyfill' // generator support  
import React from 'react'  
import ReactDOM from 'react-dom'  
import { Provider } from 'react-redux'

import App from 'app.jsx'  
import initStore from 'store'

const store = initStore()

ReactDOM.render(  
 <Provider store={ store }>
   <App />
 </Provider>,

這里我們導(dǎo)入一些依賴項(包括babel-polyfill),導(dǎo)入根組件,redux store的配置,實例化store,然后在經(jīng)過Provider class包裝的”app”div的中使用ReactDOM渲染出根組件,這樣以來,在app中所有組件樹種的react組件都可以很容易的接入到我們的store實例.

查看store.js,代碼中我們使用saga middleware來配置我們的store:

import createSagaMiddleware from 'redux-saga'

export default function () {  
 const sagaMiddleware = createSagaMiddleware()

 const store = createStore(
   rootReducer,
   applyMiddleware(sagaMiddleware)
 )

 sagaMiddleware.run(rootSaga)

 return store
}

首先我們使用createSagaMiddleware方法來創(chuàng)建middleware實例.接下來,把根reducer和middleware傳遞到createStore,這就創(chuàng)建了一個redux store.然后把我們app的root saga傳遞進(jìn)saga middleware.這一步一定要在redux store實例化以后再執(zhí)行.rootSaga是頂級generator,這個generator負(fù)責(zé)代理其他所有的generators的工作(馬上會看到.)

上面都是些什么見鬼的代碼,你個王八蛋(譯者:原意直接翻譯啊)

其實我們已經(jīng)有了有趣的東西,我們的代碼基本上依賴兩個文件.”app.jsx”是一個react組件,可以根據(jù)app的state和基于DOM事件系統(tǒng)的actions來返回渲染的html標(biāo)記.”duck.js”包含單純對象actions和reducer,這兩個函數(shù)一起工作描述出怎么修改state.其中也包含了所有的控制流代碼,控制流代碼描述了整個app的處理過程.如果你很熟悉標(biāo)準(zhǔn)的鴨子模型,我僅僅修改了鴨子模型,讓他很容易包含saga代碼.讓我們使用鴨子模塊來工作吧.

我們將會創(chuàng)建一個可以控制的時鐘.開始來想想app需要的最精簡的sate結(jié)構(gòu).在任何時間我們要詢問app的狀態(tài)是”現(xiàn)在幾點了?”所有我們需要存儲的就是單個的數(shù)字.現(xiàn)在讓我們來看看怎么改變這個狀態(tài).好的,我們我們將制作一個時鐘,用戶可以向前,向后,暫停和重置.這里的構(gòu)想意味著我們表征時間的代碼邏輯需要增,減,什么也不做,重置到0.什么事也不做意味著不需要sate發(fā)生改變,所以我們留下增加,減少,重置.我們要顯示時間的毫秒數(shù),因此app的state就定為”毫秒數(shù)”.

正如上面所講的,我們在redux代碼中使用鴨子模型,如果你不喜歡這樣做,可以分割成三個文件.
讓我們看看duck.js中的第一部分,saga actions.

 import { takeLatest } from 'redux-saga'
const initialState = {  
 milliseconds: 0
}

export default function reducer (currentState = initialState, action) {  
 switch (action.type) {
   case 'reset-clock':
     return {
       ...currentState,
       milliseconds: 0
     }
   case 'increment-milliseconds':
     return {
       ...currentState,
       milliseconds: currentState.milliseconds + 100
     }
   case 'decrement-milliseconds':
     if (!currentState.milliseconds) { return currentState }
     return {
       ...currentState,
       milliseconds: currentState.milliseconds - 100
     }
   default:
     return currentState
 }
}

export const resetClock = () => ({ type: 'reset-clock' }) 

export const incrementMilliseconds = () => ({ type: 'increment-milliseconds' })  

export const decrementMilliseconds = () => ({ type: 'decrement-milliseconds' }) 

上面這段代碼很簡單.首先由我們需求字段的起始state,接著有一個reducer,reducer實際上操作actions,它基于action type對state做出合適的修飾,之后創(chuàng)建新的state昏苏。最后我們export(模塊模式)一些可以在其他地方調(diào)用的單純action對象.(馬上我們會在saga中導(dǎo)入action對象之一).示例代碼總是這么這么的整潔.

現(xiàn)在我們需要實現(xiàn)一下app的流程.在處理過程中,什么狀態(tài)需要輸入尊沸?這個問題的另一個問法是:app在某個特定的時間應(yīng)該做什么工作?我們的時鐘可以向前,向后,暫停.為了在這幾個過程中相互轉(zhuǎn)變,我們需要三個action,開始時鐘,撥回時間,暫停時鐘.

從代碼//saga actions開始,看看duck模塊的剩余部分.我們已經(jīng)創(chuàng)建了三個actions,我們的root saga在收到某個action的時候,會啟動一個傻瓜處理流程.現(xiàn)在在代碼里傻瓜處理流程只是打印一下action的名字.后續(xù)我們會開始根據(jù)action type處理具體的增,減,休眠流程.這里是duck.js的saga代碼.

 // saga actions
export const startClock = () => ({ type: 'start-clock' })  
export const pauseClock = () => ({ type: 'pause-clock' })  
export const rewindClock = () => ({ type: 'rewind-clock' })

// saga
export function* rootSaga () {  
 yield takeLatest(['start-clock', 'pause-clock', 'rewind-clock'], handleClockAction)
}

function* handleClockAction ({ type }) {  
 console.log('Pushed this action to handleClockAction: ', type)
}

actions(嚴(yán)格上講,根據(jù)術(shù)語來說應(yīng)該是叫“action creators”,但是無所謂,只要你理解具體的意義就可以)應(yīng)該看起來和其他的redux actions類似.但是這些action在我們的reducer中不能得到處理.如果保持僅僅在saga代碼附近保留這些actions,這里的acions僅僅觸發(fā)saga.做到這一點,會避免action和根據(jù)這些action做出的state修改的代碼混雜在一起.顯而易見,saga action仍然通脫connect函數(shù)綁定到store實例,并且輸入到組件里.

現(xiàn)在解釋一下這個文件里奇怪的saga.你還記得rootSaga被傳遞到saga中間件,對嗎捷雕?坦率講,你可能也不知道,但是這也沒關(guān)系.每次我們發(fā)出一個action,action會被推送到經(jīng)過sagaMiddleware.run(generator)包裝的generator.這就意味著,每個generator都有機(jī)會響應(yīng)action,在我們的實例中,rootSaga遇到匹配的action type的時候才會做出響應(yīng).我們正在使用從Redux Saga獲取的takeLatest助手函數(shù)完成這個工作.takeLatest接收任何與action type數(shù)組匹配的action,然后接著傳遞他,啟動一個handleClockAcion流程,傳遞進(jìn)action.takeLatest意思是直接收最新的action椒丧,如果現(xiàn)在還有正在運行的handleClockAction的話,在新的action開始之前,當(dāng)前的這個處理流程需要先退出.handleClockAction,本質(zhì)上是在后臺啟動,允許rootSaga保持運行狀態(tài),即使handleClockAction仍在運行,也可以接受下一個匹配的action.

注意我們使用的yield關(guān)鍵詞,回想一下,yield在generator中發(fā)出和接收值.在任何時間,我們yield一個Redux Saga助手或者effect的時候,我們就正在和Saga middleware進(jìn)行通訊.在我們的上面的實例中,Redux Saga等待匹配發(fā)送到saga的action.后面我們還會更進(jìn)一步深入討論.
我希望你至少對這個流程有一點感覺.我認(rèn)為可能在測試過程中(譯者:這里的意思是實際運行代碼的過程,并不是代碼的測試過程)你對這個流程更清楚一點.所以讓我們看看React組件中怎么和用戶進(jìn)行交互的過程.
在組件這一點看,“app.jsx”是非常簡單的react組件.讓我們看個仔細(xì).

import React from 'react'  
import { connect } from 'react-redux'

import { incrementMilliseconds, decrementMilliseconds, resetClock, startClock, pauseClock, rewindClock } from 'duck'

class Clock extends React.Component {  
 render () {
   const {
     milliseconds,
     incrementMilliseconds,
     decrementMilliseconds,
     resetClock,
     startClock,
     pauseClock,
     rewindClock
   } = this.props

   
   return (
     <div>
       <svg onClick={ incrementMilliseconds } onDoubleClick={ resetClock } onMouseLeave={ decrementMilliseconds }
         className="clock" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="500">
         <circle cx="50" cy="50" r={ 30 } stroke={ 'rgba(1,1,1,1)' } fill="orange" />
       </svg>
       <p>{ milliseconds }</p>
       <p>
         <button type="button" onClick={ startClock }>Start Clock</button>
         <button type="button" onClick={ pauseClock }>Pause Clock</button>
         <button type="button" onClick={ rewindClock }>Rewind Clock</button>
       </p>
     </div>
   )
 }
}

export default connect(state => ({  
 milliseconds: state.milliseconds
}), ({
 incrementMilliseconds,
 decrementMilliseconds,
 resetClock,
 startClock,
 pauseClock,
 rewindClock
}))(Clock)
  

通過使用connect高內(nèi)聚組件,我們可以從store的state獲取一個字段,并作為props傳遞進(jìn)入組件.我們也通過一個對象傳遞四個action creators.Redux把這個對象綁定到store實例中,確保我們在組件中調(diào)用這幾個action的時候,他們可以正確的被dispatch.
在我們的渲染中,我們返回一個<div>,這個元素中有一個SVG(后續(xù)中將會比較關(guān)鍵).SVG有一些事件操作句柄,這些操作句柄將會dispatch state修飾actions.接下來,會有一個<p>元素依據(jù)app的state來顯示當(dāng)前時間.最后我們有幾個<button>s 連接到saga的actions.
上面的代碼都就位以后,我們就試著運行一下app,驗證一下基礎(chǔ)構(gòu)架和actions的工作情況.

到底能工作嗎救巷?

回到你的終端,運行npm start.現(xiàn)在輸入localhost:8080,在瀏覽器中打開devtools,檢查一下js 終端.當(dāng)你點擊buttons的時候,會看到saga actions的日志輸出.現(xiàn)在試著在SVG上點擊,鼠標(biāo)一定,雙擊action.你可以看到毫秒文本的更新.

app的界面

真好啊,我們創(chuàng)建了一個Redux Saga app的模板結(jié)構(gòu),了解了怎么使用takeLatest.還可以在終端中輸出一些日志信息.棒壶熏!
在下一篇文章中,我們會完成整個時鐘的實施,得到一些非常酷的內(nèi)容.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浦译,一起剝皮案震驚了整個濱河市棒假,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌精盅,老刑警劉巖帽哑,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叹俏,居然都是意外死亡妻枕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人只壳,你說我怎么就攤上這事救氯。” “怎么了?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我饵撑,道長,這世上最難降的妖魔是什么唆貌? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任滑潘,我火速辦了婚禮,結(jié)果婚禮上锨咙,老公的妹妹穿的比我還像新娘众羡。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布粱侣。 她就那樣靜靜地躺著羊壹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪齐婴。 梳的紋絲不亂的頭發(fā)上油猫,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天,我揣著相機(jī)與錄音柠偶,去河邊找鬼情妖。 笑死,一個胖子當(dāng)著我的面吹牛诱担,可吹牛的內(nèi)容都是我干的毡证。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼蔫仙,長吁一口氣:“原來是場噩夢啊……” “哼料睛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摇邦,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤恤煞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后施籍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體居扒,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年丑慎,在試婚紗的時候發(fā)現(xiàn)自己被綠了喜喂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡竿裂,死狀恐怖玉吁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铛绰,我是刑警寧澤诈茧,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布产喉,位于F島的核電站捂掰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏曾沈。R本人自食惡果不足惜这嚣,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞俱。 院中可真熱鬧姐帚,春花似錦、人聲如沸障涯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至九秀,卻和暖如春遗嗽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼓蜒。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工痹换, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人都弹。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓娇豫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親畅厢。 傳聞我的和親對象是個殘疾皇子冯痢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,595評論 2 350

推薦閱讀更多精彩內(nèi)容