Skip to content

Instantly share code, notes, and snippets.

@deepak
Created August 9, 2016 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deepak/e8842c38e5b9fdbd9029a2d96cca9593 to your computer and use it in GitHub Desktop.
Save deepak/e8842c38e5b9fdbd9029a2d96cca9593 to your computer and use it in GitHub Desktop.
translate redux-saga readme from traditional chinese
#### note
translate https://github.com/yelouafi/redux-saga/blob/7da88a9096268ed70cfd9e6f0a54454274ad5ddd/README_zh-hant.md
using google translate
# Redux-saga
[! [Npm version] (https://img.shields.io/npm/v/redux-saga.svg?style=flat-square)] (https://www.npmjs.com/package/redux-saga )
Another Side Effect model Redux app. Instead of thunk redux-thunk sent. You can create * * Sagas in one place to focus all Side Effect logic.
App logic will exist in two places:
- Reducers responsible for state transition between the actions.
- Sagas responsible for orchestrating the complex / non-synchronous operation.
Use Generator function to create Sagas.
> Next content, you will see. Generators, although looks low-level (low level) than the ES7 aysnc function, but it can provide such declarative action (delcarative effects), cancellation (cancellation) and other functions. These difficulties can not function with a simple async function to make real.
This middleware is proposed
- Combinable abstract ** Effect **: waiting for an action; Trigger State change (through the actions assigned to the store); call a distal services; and other forms of Effects. Saga can use a familiar process control composition (if, while, for, try / catch) a combination of these Effects.
- Sage itself a Effect. Possible by using coordinator (combinators) to other combinations of Effects. It may also be other Sagas call, and provide maximum Subroutines [Structured Programming] (https://en.wikipedia.org/wiki/Structured_programming).
- Effects may be in the presentation (declaratively) caused (yielded). Effect is caused by your description of middleware will be responsible for implementing it. Let your logic within the Generators can be adequately tested.
- You can implement complex logic operations, across multiple actions (for example: User induction training wizard dialog, complex rules of the game ⋯), these non-expression.
- [Getting Started] (# getting-started)
- [Wait for future actions] (# waiting-for-future-actions)
- [Delivered actions to store] (# dispatching-actions-to-the-store)
- [Common abstract: Effect] (# a-common-abstraction-effect)
- [Declarative Effects] (# declarative-effects)
- [Error handling] (# error-handling)
- [Effect coordinator] (# effect-combinators)
- [Through yield * of sequential Sagas] (# sequencing-sagas-via-yield)
- [Combination Sagas] (# composing-sagas)
- [Non-blocking call - fork / join] (# non-blocking-calls-with-forkjoin)
- [Cancel task] (# task-cancellation)
- [Dynamic Start Sagas - runSaga] (# dynamically-starting-sagas-with-runsaga)
- [Established paradigm from source] (# building-examples-from-sources)
- [In browsers umd formation] (# using-umd-build-in-the-browser)
# Getting Started
installation
`` `
npm install redux-saga
`` `
Create Saga (using Redux counter examples)
`` `Javascript
import {take, put} from 'redux-saga'
// Sagas / index.js
function * incrementAsync () {
  while (true) {
    // Wait for each INCREMENT_ASYNC action
    const nextAction = yield take (INCREMENT_ASYNC)
    // Delay is an example of the function
    // Return Promise, in milliseconds (ms) after solving (resolves) specified
    yield delay (1000)
    // Assignment INCREMENT_COUNTER
    yield put (increment ())
  }
}
export default [incrementAsync]
`` `
The redux-saga received middleware pipeline
`` `Javascript
// Store / configureStore.js
import sagaMiddleware from 'redux-saga'
import sagas from '../sagas'
export default function configureStore (initialState) {
  // Note: passing middleware as the last argument to createStore requires redux @> = 3.1.0
  return createStore (
    reducer,
    initialState,
    applyMiddleware (/ * other middleware, * / sagaMiddleware (... sagas))
  }
}
`` `
# Wait for future actions
The previous example, we created a `wait for future actions` Saga. Wherein `yield take (INCREMENT_ASYNC)` call is a typical example of how a Sagas works.
Typically, such control is actually constituted by middleware Effect who triggered by the Action Creator. For example, redux-thunk control * thunks *, and `(getState, dispatch)` as a parameter into, redux-promise in control Promises, its assigned value resolved. redux-gen control generators, dispatched all causes (yielded) the actions to store into. Here all of the middleware has a common feature is the 'call by each action' style. When the action occurs, they will be called again and again, in other words, they are triggered by the scope of their * root action * decision.
Sagas different mode of operation is not triggered by the Action Creators, but with your application and decide together which actions users need to pay attention (watch). Like in the service running in the background, choose their own logical progression. In the example above, `incrementAsync` use` yield take (...) `to pull * *` INCREMENT_ASYNC` action. This is a blocking call * * indicates Saga not proceed until you receive matching action.
Above using `take (INCREMENT_ASYNC)` form, he said it is waiting for the `INCREMENT_ASYNC` type of action.
`Take` support several styles in order to meet the constraints of the actions. `Yield take (PATTERN)` call control performed according to the following rules:
- When the PATTERN is undefined or `` * ''. All actions will be in line with new entrants (for example, `take ()` will match all of the actions)
- When the PATTERN is a function, only when PATTERN (action) is true, action will be matched. (For example, `take (action => action.entities)` `entities` There will match all fields of action).
- When the PATTERN is a string, only when action.type === PATTERN to match only when (as in the above examples `take (INCREMENT_ASYNC)`)
- When the PATTERN is an array, only when action.type comply with one of the elements in the array will not match (for example, `take ([INCREMENT, DECREMENT])` `INCREMENT` will match or` DECREMENT`.)
# Delivery actions to store
After receiving the query action, Saga trigger `delay (1000)` call, in this example, the return of the Promise, and it will be resolved in 1 second. This is a set of plug call, so the Saga will continue to wait for 1 second.
After the delay, Saga use `put (action)` function assigned a `INCREMENT_COUNTER` action. Similarly, there will also be awaiting the results after dispatched. If the call is dispatched return of a general value, Saga immediate restoration to continue (asap), but if it is a Promise, Promise will be waiting to solve (or reject).
# Common abstract: Effect
To generalize, waiting for future action; wait for future results, such as call `yield delay (1000)`; or wait for the results assigned, it is the same concept. All cases are caused by some form of Effects.
The Saga things do, in fact, is to combine all these effects, in order to implement the desired control process. The easiest way is a yeidls after another yields, causing progressive Effects. You can also use the familiar control flow operation sub (if, while, for) to implement complex control flow. Or if you want to use Effects Coordinator to express concurrency (concurrency, yield race) and parallel (parallelism, yield [...]). It can even cause other Sagas, so you have a strong routine / subroutine style.
For example, `incrementAsync` use infinite loop` while (true) `will always be represented in the operation in the application life cycle.
You can also create Sagas limited time. For example, following the former Saga wait three `INCREMENT_COUNTER` actions and triggers` showCongratulation () `action, it will end after the meet.
`` `Javascript
function * onBoarding () {
  for (let i = 0; i <3; i ++)
    yield take (INCREMENT_COUNTER)
  yield put (showCongratulation ())
}
`` `
# Declarative Effects
Sagas Generators can cause various forms of Effects. The simplest is to cause Promise
`` `Javascript
function * fetchSaga () {
  // Fetch is a simple function
  // Return Promise will solve GET response
  const products = yield fetch ( '/ products')
  // Assignment RECEIVE_PRODUCTS action
  yield put (receiveProducts (products))
}
`` `
In the example above, `fetch ( '/ products')` Return Promise will solve GET responses, so 'fetch effect' immediately implemented. Simple and idiomatic, but ⋯
Suppose we want to test the above generator
`` `Javascript
const iterator = fetchSaga ()
assert.deepEqual (iterator.next (). value, ??) // what to expect results?
`` `
We want to check the results of the first generator caused in this case is to perform `fetch ( '/ products')` after the results. Test to perform the actual service is not a viable way, nor is it the practice of the method, so we need * this * generic fetch service, in other words, we need to replace the fake real `fetch` method, false and does not actually issue GET requests, but to check that with the correct parameters (in this case, the correct parameters for the ` '/ products'`) call` fetch`.
Imitation make the test more difficult and less reliable. On the other hand, a simple function return value to make the test easier to simply use `equal ()` to check the results. This is the most reliable writing test method.
Do not believe? You are encouraged to read [Eric Elliott this article] (https://medium.com/javascript-scene/what-every-unit-test-needs-f6cd34d9836d#.4ttnnzpgc).
> (...) `Equal ()`, in essence answer two of the most important questions, each unit test must be answered, but most will not answer:
>
> - What is the actual output that?
> - The output is what to expect?
>
> If you complete a test but did not answer these two questions, it is not a true unit tests. You only have a sloppy, incomplete test.
And what we really want, just need to make sure the call `fetchSaga` caused function and its call parameters are correct. Therefore, this library provides a number of declarative way to cause Side Effects, make it easier to test the logic of Saga
`` `Javascript
import {call} from 'redux-saga'
function * fetchSaga () {
  const products = yield call (fetch, '/ products') // effect will not be executed
}
`` `
This uses the `call (fn, ... args)` function. ** Differences to the previous example that does not perform a call fetch instantly, replaced, `call` create the effect description **. As you Redux, use action creators create object described action, will be executed Store, `call` create object description function call. redux-saga agency responsible for software function call and respond with resolve and then start generator.
This allows us outside Redux environment can be easily tested Generator.
`` `Javascript
import {call} from 'redux-saga'
const iterator = fetchSaga ()
assert.deepEqual (iterator.next (). value, call (fetch, '/ products')) // is contemplated that call (...) value
`` `
Now, no longer need to imitate anything. Simple equality testing is sufficient.
Advantages effects statement is to test all of the logic in the Saga / Generator, and simply repeated to check the result value of iterations, the values ​​are consistently simple equality test. The real benefit is that complex non-synchronous operation is no longer a black box, no matter how complex can be tested in detail,
Some call the object's method (ie, using `new` creation), you can provide the following form` this` context (context) to the calling function
`` `Javascript
yield call ([obj, obj.method], arg1, arg2, ...) // as we use obj.method (arg1, arg2 ...)
`` `
`Apply` is a pseudonym for a method call form
`` `Javascript
yield apply (obj, obj.method, [arg1, arg2, ...])
`` `
`Call` and` apply` suitable return Promise of function. Other functions can be used to handle `cps` Node style functions (for example,` fn (... args, callback) `` callback` which is `(error, result) => ()` form). for example
`` `Javascript
import {cps} from 'redux-saga'
const content = yield cps (readFile, '/ path / to / file')
`` `
Of course, you can also test it
`` `Javascript
import {cps} from 'redux-saga'
const iterator = fetchSaga ()
assert.deepEqual (iterator.next (). value, cps (readFile, '/ path / to / file'))
`` `
`Cps` also support the same method call form, as` call` same.
# Error Handling
Generator which can use a simple try / catch statement to catch errors. In the following example, Saga from `api.buyProducts` call (ie rejected Promise) to capture error
`` `Javascript
function * checkout (getState) {
  while (yield take (types.CHECKOUT_REQUEST)) {
    try {
      const cart = getState (). cart
      yield call (api.buyProducts, cart)
      yield put (actions.checkoutSuccess (cart))
    } Catch (error) {
      yield put (actions.checkoutFailure (error))
    }
  }
}
`` `
Of course, not force you to use a try / catch block to handle your API error, you can let the general API service return value with an error flag
`` `Javascript
function buyProducts (cart) {
  return doPost (...)
    .then (result => {result})
    .catch (error => {error})
}
function * checkout (getState) {
  while (yield take (types.CHECKOUT_REQUEST)) {
    const cart = getState (). cart
    const {result, error} = yield call (api.buyProducts, cart)
    if (! error)
      yield put (actions.checkoutSuccess (result))
    else
      yield put (actions.checkoutFailure (error))
  }
}
`` `
#Effect Coordinator
`Yield` statement very fit to represent asynchronous flow control, a simple and linear style. But we also need operating in parallel. Can not simply write
`` `Javascript
// Error, effects will be performed sequentially
const users = yield call (fetch, '/ users'),
      repose = yield call (fetch, '/ repose')
`` `
Because until a first call resolution, and the second effect will not be executed. Instead, we should be rewritten
`` `Javascript
import {call} from 'redux-saga'
// Correct, effects will be executed in parallel
const [users, repose] = yield [
  call (fetch, '/ users'),
  call (fetch, '/ repose')
]
`` `
When we cause a array of effects, generator will block until all the effects are resolved (or if one of them was rejected as `Promise.all` behavior).
Sometimes multiple tasks in parallel issue did not want to wait for all tasks to be solved, but only one winner * *: The first is to solve (or reject). `Race` function provides a contest between the functionality of multiple effects.
The following example shows Saga triggered a remote fetch request, and the request to limit in 1 second timeout.
`` `Javascript
import {race, take, put} from 'redux-saga'
function * fetchPostsWithTimeout () {
  while (yield take (FETCH_POSTS)) {
    // Send out competition between two effects
    const {posts, timeout} = yield race ({
      posts: call (fetchApi, '/ posts'),
      timeout: call (delay, 1000)
    })
    if (posts)
      put (actions.receivePosts (posts))
    else
      put (actions.timeoutError ())
  }
}
`` `
Through yield * # sequentiality Sagas
You can use the built-in `yield *` sub operate sequentially combining multiple sagas. So that you can use a simple program to sequential execution-style * macro-tasks *
`` `Javascript
function * playLevelOne (getState) {...}
function * playLevelTwo (getState) {...}
function * playLevelThree (getState) {...}
function * game (getState) {
  const score1 = yield * playLevelOne (getState)
  put (showScore (score1))
  const score2 = yield * playLevelTwo (getState)
  put (showScore (score2))
  const score3 = yield * playLevelThree (getState)
  put (showScore (score3))
}
`` `
Note the use of `yield *` will lead to the spread during the execution of JavaScript * * entire sequence. Iterator result would cause nested iterators. Instead of the more powerful approach is to use a more generalized middleware formation mechanism.
# Combination Sagas
Although the use of `yield *` to provide an idiomatic way to combine Sagas. But the way some limitations:
- You may want to separate test nested generators. This led to some duplicate test code generation and loss repeated. We do not want to perform a nested generator, just want to make sure that the correct parameters call.
- More importantly, `yield *` allows only sequential nature of tasks, you can only yield * a generator.
You can simply use `yield` to start a parallel or multiple sub-tasks. When caused by a call to the generator, Saga will wait generator termination process began, then use the return value before you start (or throw an error when an error from the sub-task).
`` `Javascript
function * fetchPosts () {
  yield put (actions.requestPosts ())
  const products = yield call (fetchApi, '/ products')
  yield put (actions.receivePosts (products))
}
function * watchFetch () {
  while (yield take (FETCH_POSTS)) {
    yield call (fetchPosts) // Wait fetchPosts mission ends
  }
}
`` `
Causing a nested array generatos will start all sub-generators in parallel and wait completed. Followed by the value of all the results before you start
`` `Javascript
function * mainSaga (getState) {
  const results = yield [call (task1), call (task2), ...]
  yield put (showResults (results))
}
`` `
In fact, the cause Sagas and cause other effects are no different (future actions, overtime ⋯). This means you can use a combination of these Sagas effect regulator and other types.
For example, you want to allow the user to complete some of the game within the time limit
`` `Javascript
function * game (getState) {
  let finished
  while (! finished) {
    // Must be completed within 60 seconds
    const {score, timeout} = yield race ({
      score: call (play, getState),
      timeout: call (delay, 60000)
    })
    if (! timeout) {
      finished = true
      yield put (showScore (score))
    }
  }
}
`` `
# Non-blocking call - fork / join
`Yield` statement effect caused by generator suspended until the cause has been resolved or rejected. If you're a little closer look at this example
`` `Javascript
function * watchFetch () {
  while (yield take (FETCH_POSTS)) {
    yield put (actions.requestPosts ())
    const posts = yield call (fetchApi, '/ posts') // call blocking
    yield put (actions.receivePosts (posts))
  }
}
`` `
`WatchFetch` generator will wait until the` yield call (fetchApi, '/ posts') `end. Imagine `FETCH_POSTS` through a` `reformer button triggers. If we fetch between each application Close button (non-concurrent fetch) so that there is no problem, because we know there will be additional `FETCH_POSTS` action trigger until a response` fetchApi` call.
However, if the application you want to allow a user clicks on the button `` reforming, without the need to wait for the end of the current request?
The following example describes a possible sequence of events
`` `
UI watchFetch
-------------------------------------------------- ------
FETCH_POSTS ..................... Call fetchApi ............ waiting for a solution
.................................................. ......
.................................................. ......
FETCH_POSTS ............................................. omission
.................................................. ......
FETCH_POSTS ............................................. omission
................................ FetchApi return ............
.................................................. ......
`` `
When `watchFetch` in` fetchApi` call blocking, all calls and responses between the occurrence of `FETCH_POSTS` will be missed.
To express non-blocking calls, we can use `fork` function. `Fork` rewritten with a possible wording of the preceding example is
`` `Javascript
import {fork, call, take, put} from 'redux-saga'
function * fetchPosts () {
  yield put (actions.requestPosts ())
  const posts = yield call (fetchApi, '/ posts')
  yield put (actions.receivePosts (posts))
}
function * watchFetch () {
  while (yield take (FETCH_POSTS)) {
    yield fork (fetchPosts) // non-blocking call
  }
}
`` `
`Fork`, like` call`, accepted the function / generator call.
`` `Javascript
yield fork (func, ... args) // simple asynchronous function (...) -> Promise
yield fork (generator, ... args) // Generator function
`` `
`Yield fork (api)` The result is a sub-task description * *. In order to be able to wait to obtain forked task results, we use `join` function
`` `Javascript
import {fork, join} from 'redux-saga'
function * child () {...}
function * parent () {
  // Non-blocking call
  const task = yield fork (subtask, ... args)
  // Wait ...
  // Now call blocking, will start again with a result of a task
  const result = yield join (task)
}
`` `
Task object open several useful way
<Table>
  <Tr>
    <Th> Method </ th>
    <Th> return value </ th>
  </ Tr>
  <Tr>
    <Td> task.isRunning () </ td>
    <Td> return true when the task is not yet return, or throw an error </ td>
  </ Tr>
  <Tr>
    <Td> task.result () </ td>
    <Td> tasks return results. `Undefined` when the task is still executing </ td>
  </ Tr>
  <Tr>
    <Td> task.error () </ td>
    <Td> task throws an error. `Undefined` when the task is still executing </ td>
  </ Tr>
  <Tr>
    <Td> task.done </ td>
    <Td>
      One of the following two Promise
        <Ul>
          <Li> solve tasks with return value </ li>
          <Li> Wrong refuse thrown with the task </ li>
        </ Ul>
      </ Td>
  </ Tr>
</ Table>
# Task Cancel
When a task has started, you can use the `yield cancel (task)` to stop the execution. Cancel the execution of the tasks will be thrown `SagaCancellationException` lead to internal error.
To see how it works, let's consider a simple example. A background synchronization feature, you can through certain UI commands to start / pause. The receiving `START_BACKGROUND_SYNC` action, we will start a background task, periodically synchronize some data from a remote server.
Mission duration of execution until `STOP_BACKGROUND_SYNC` action trigger. Then it will cancel background tasks again and wait for the next `START_BACKGROUND_SYNC` action.
`` `Javascript
import {take, put, call, fork, cancel, SagaCancellationException} from 'redux-saga'
import actions from 'somewhere'
import {someApi, delay} from 'somewhere'
function * bgSync () {
  try {
    while (true) {
      yield put (actions.requestStart ())
      const result = yield call (someApi)
      yield put (actions.requestSuccess (result))
      yield call (delay, 5000)
    }
  } Catch (error) {
    if (error instanceof SagaCancellationException)
      yield put (actions.requestFailure ( 'Sync cancelled!'))
  }
}
function * main () {
  while (yield take (START_BACKGROUND_SYNC)) {
    // Start a task execution in the background
    const bgSyncTask = yield fork (bgSync)
    // Wait for user to stop action
    yield take (STOP_BACKGROUND_SYNC)
    // User clicked to stop. Cancel background tasks
    // This will throw SagaCancellationException exception to the background task
    yield cancel (bgSyncTask)
  }
}
`` `
`Yield cancel (bgSyncTask)` `SagaCancellationException` will be thrown among the tasks currently performed. In the example above, the exception is captured by `bgSync`. ** Note **: uncaught SagaCancellationException not emerge upwards. As the example above, if the `bgSync` canceled uncaught exception, the exception will not propagate to` main` (because `main` have to continue down).
Cancel the execution of the tasks will be to abolish the present effect, it is to cancel the current task.
For example, if at some point in time application life cycle, we have a pending call chain
`` `Javascript
function * main () {
  const task = yield fork (subtask)
  ...
  // Wait
  yield cancel (task)
}
function * subtask () {
  ...
  yield call (subtask2) // this call is currently blocked
  ...
}
function * subtask2 () {
  ...
  yield call (someApi) // this call is currently blocked
  ...
}
`` `
`Yield cancel (task)` triggers cancel `subtask`, then trigger cancel` subtask2`. `SagaCancellationException`` subtask2` will be thrown into, and then thrown into the `subtask`. If `subtask` the cancellation exception handling is omitted, console will display a warning message to alert developers (message only when the variable is set to` process.env.NODE_ENV` ` 'development'` time will be displayed)
Cancel exceptions main purpose is to allow the cancellation of the tasks you can perform cleanup logic. This allows the app will not leave in a state of inconsistency, in the example above background synchronized through the capture canceled exception, `bgSync`` requestFailure` action can be assigned to the store. Otherwise, store may be left in an inconsistent state (for example, waiting for the results of pending requests)
> A very important thing to remember `yield cancel (task)` will not wait for the completion of the task to be canceled (to perform within the catch block). cancel effect acts like a fork. This will return once cancel is initialized.
> Upon cancellation, under normal circumstances, to be completed as soon as possible to clean up the logic. In some cases, cleanup logic may involve some non-synchronous operation, but canceled the task of the existence of a separate process, there is no way back to rejoin the main control flow (via Redux store in addition to other tasks assigned to actions. However, this will lead to complex control flow, it is difficult reasoning better way is as fast as possible after the cancellation of the task).
## Automatic cancellation
In addition to manually cancel. There are some cases triggered automatically canceled
1- `race` effect in. All race contenders, in addition to the winner, and the rest are automatically canceled.
2 - In parallel effect ( `yield [...]`) in. Once there is a sub-effects is denied, the parallel effect will soon be denied (as Promise.all). Under such circumstances, all other sub-effects will be automatically canceled.
# Dynamic start Sagas - runSaga
`RunSaga` letter shown so that you can begin sagas outside Redux middleware environment. Also allows you to hook to an external input / output, is different from the store actions.
For example, you can start at the servo end of a Saga
`` `Javascript
import serverSaga from 'somewhere'
import {runSaga, storeIO} from 'redux-saga'
import configureStore from 'somewhere'
import rootReducer from 'somewhere'
const store = configureStore (rootReducer)
runSaga (
  serverSaga (store.getState),
  storeIO (store)
) .done.then (...)
`` `
`RunSaga` return a task object. `Fork` effect is like a return of the same.
And obtaining and assigned action to a different store, `runSaga` also be connected to other input / output sources. So you can also use the sagas features to implement your control flow in the world outside of Redux.
This method follows function signature
`` `Javascript
runSaga (iterator, {subscribe, dispatch}, [monitor])
`` `
parameter
- `Iterator: {next, throw}`: iterator object, typically created using Generator
- `Subscribe (callback) => unsubscribe`: in other words, to accept the letter shown callback function shown, return unsubscribe letter shows
  - `Callback (action)`: callback function shown (by runSaga) to subscribe to input events. `Subscribe` must support multiple subscribers register
  - `Unsubscribe ()`: `runSaga` by the use, once the input source is complete, to cancel the subscription (generally return or throw an exception)
- `Dispatch (action) => result`: used to implement` put` effects. Whenever the issue `yield put (action)`, `dispatch`
  It will be called with the `action`. `Dispatch` return value will be used to implement` put` effect. Promise results are automatically resolved / rejected.
- `Monitor (sagaAction)` (optional): used to dispatch all Saga related events callback function is shown. In the version of the middleware, all actions will be assigned to the Redux store. See [sagaMonitor Use Example]
  (Https://github.com/yelouafi/redux-saga/blob/master/examples/sagaMonitor.js).
`Subscribe` used to implement` take (action) `effect. Whenever `subscribe` action to issue its callback function shows all sagas will be` take (PATTERN) `obstruction, obtained in line with the current style will enter the action before starting operation.
# Source code form from Example
`` `
git clone https://github.com/yelouafi/redux-saga.git
cd redux-saga
npm install
npm test
`` `
The following example (of a degree) repository migration from Redux
Counter example
`` `
npm run counter
// Generator test
npm run test-counter
`` `
Shopping Cart Example
`` `
npm run shop
// Generator test
npm run test-shop
`` `
Asynchronous examples
`` `
npm run async
// Sorry, no test
`` `
Real-world examples (including webpack hot reloading)
`` `
cd examples / real-world
npm install
npm start
`` `
# Set up your browser to use umd
`Redux-saga` have ** umd ** formation located beneath` dist / `directory. Use `redux-saga` of umd form can be obtained from` ReduxSaga` window object under. When you do not use webpack or browserify, umd version is very useful, you can get directly from [npmcdn] (npmcdn.com). It contains the following form:
- [Https://npmcdn.com/redux-saga/dist/redux-saga.js](https://npmcdn.com/redux-saga/dist/redux-saga.js)
- [Https://npmcdn.com/redux-saga/dist/redux-saga.min.js](https://npmcdn.com/redux-saga/dist/redux-saga.min.js)
**important! ** If your browser does not support target _es2015 generators_, you need to provide appropriate polyfill, for example, * babel * provided: [browser-polyfill.min.js] (https://cdnjs.cloudflare.com/ ajax / libs / babel-core / 5.8.25 / browser-polyfill.min.js). This must be loaded before polyfill ** redux-saga **.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment