{"id":23897593,"url":"https://github.com/boostup/redux-saga-playground","last_synced_at":"2026-06-19T11:02:17.406Z","repository":{"id":143741389,"uuid":"312981269","full_name":"boostup/redux-saga-playground","owner":"boostup","description":null,"archived":false,"fork":false,"pushed_at":"2020-11-15T08:13:45.000Z","size":219,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-14T11:34:58.851Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/boostup.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-11-15T07:36:55.000Z","updated_at":"2020-11-15T08:13:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"4618cc21-fcc6-4ca7-910b-36184385a67b","html_url":"https://github.com/boostup/redux-saga-playground","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/boostup/redux-saga-playground","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostup%2Fredux-saga-playground","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostup%2Fredux-saga-playground/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostup%2Fredux-saga-playground/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostup%2Fredux-saga-playground/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/boostup","download_url":"https://codeload.github.com/boostup/redux-saga-playground/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/boostup%2Fredux-saga-playground/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34528144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-04T17:16:46.534Z","updated_at":"2026-06-19T11:02:17.400Z","avatar_url":"https://github.com/boostup.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-saga-beginner-tutorial\n\nCompanion Repo for [Redux/Redux-saga beginner tutorial](https://github.com/redux-saga/redux-saga/blob/master/docs/introduction/BeginnerTutorial.md)\n\n# Note taken while doing tutorial\n\n## Introduction\n\nhttps://redux-saga.js.org/docs/introduction/BeginnerTutorial.html\n\n### Sagas\n\n- **Sagas** are implemented as generator functions that yield objects to the redux-saga middleware.\n- The **yielded objects** are a kind of instruction to be interpreted by the middleware\n- When a promise is yielded, the middleware will suspend the Saga untile the Promise completes\n- Once the Promise is resolved, the middleware resumes the Saga, executing code untile the next yield\n\n### Effects\n\n- `put` (as in `put({type: \"INCREMENT\"})`) is an Effect in the saga jargon\n- **Effects** are plain JS objects which contain instructions to be fulfilled by the middleware\n- When a middleware retrieves an Effect yielded by a Saga, the Saga is paused until the Effect is fulfilled\n\n### Listeners\n\n`takeEvery`, `takeLatest` etc, are helper functions provided by `redux-saga` to listen for dispatched actions, ie:\n\n```\n//listens for dispatched `INCREMENT_ASYNC` actions and run `incrementAsync` each time\nexport function* watchIncrementAsync() {\n  yield takeEvery(\"INCREMENT_ASYNC\", incrementAsync);\n}\n\n```\n\n```\n// export a single entry point to start all Sagas at once.  this is an array with the results of calling the sagas. this means the each (2 in this case, because there are 2 sagas) of the resulting Generators will be started in parallel\nexport default function* rootSaga() {\n  yield all([\n    //\n    helloSaga(),\n    watchIncrementAsync(),\n  ]);\n}\n```\n\n### Iterator objects\n\n`incrementAsync` is a generator function. When run, it returns an iterator object, and the iterator's `next` method returns an object with the following shape:\n\n```\ngen.next() // =\u003e {done: boolean, value: any}\n```\n\n- The `value` field contains the yielded expression, meaning the result of the expression after the yield.\n- The `done` field indicates if the generator has terminated or if there are still more 'yield' expressions.\n\nIn the case of `incrementAsync`, the generator yields 2 values consecutively :\n\n1. `yield delay(1000)`\n2. `yield put({type: \"INCREMENT\"})`\n\nSo, invoking the `next` method 3 times provides the following results:\n\n```\ngen.next() // =\u003e {done: false, value: \u003cresult of calling `delay(1000)`\u003e}\n\ngen.next() // =\u003e {done: false, value: \u003cresult of calling `put({type: \"INCREMENT\"})`\u003e}\n\ngen.next() // =\u003e {done: true, value: undefined}\n\n```\n\n### Testing the saga\n\nThe problen is that `yield delay(1000)` does not yield a normal value, meaning, we can't do a simple equality test on Promises, which is what delay returns.\n\n#### `call` to the rescue\n\n`redux-saga` provides a way to make this possible. Instead of calling `delay(1000)` directly inside `incrementAsync`, we'll call it _indirectly_ and export it to make a subsequent deep comparison possible by changing this `yield delay(1000)` to `yield call(delay, 1000)`.\n\nSo the new `incrementAsync` function is:\n\n```\nexport function* incrementAsync() {\n  // use the call Effect\n  yield call(delay, 1000)\n  yield put({ type: 'INCREMENT' })\n}\n```\n\n- So when the caller (the middleware or the test runner) iterates over this generator function, it no longer gets a _Promise_, but and _Effect_.\n\n- This Effect is an instructions for the caller to call a given function with the given arguments.\n\n- Effect like `put` and `call` do NOT perform any dispatch or async cal themselves; instead, they return plain JS objects :\n\n```\nput({type: \"INCREMENT\"}) // =\u003e {PUT: {type: \"INCREMENT\"}}\n\ncall(delay, 1000) // =\u003e {CALL: {fn: delay, args: [1000]}}\n```\n\nThe called examines the type of each yielded Effect to decide how to fulfill each one of them :\n\n- if the Effect type is a `PUT` =\u003e it dispatches an action to the redux Store.\n- if the Effect type is a `CALL` =\u003e it calls the given function\n\n**The separation between _Effect creation_ and _Effect execution_ makes it possible to test our Generator in a surprisingly easy way ** (see the `sagas.spec.js` file) :\n\n- Since `put` and `call` return plain objects, we can reuse the same functions in our test code. And to test the logic of `incrementAsync`, we iterate over the generator and do `deepEqual` tests on its values.\n\n- `npm test` to run the tests\n\n## Basic concepts\n\nhttps://redux-saga.js.org/docs/basics/UsingSagaHelpers.html\n\nTake an example of pressing a button which dispatches a \"FETCH_REQUESTED\" action to fetch some async data.\n\n```\nimport { call, put } from 'redux-saga/effects'\n\nexport function* fetchData(action) {\n   try {\n      const data = yield call(Api.fetchUser, action.payload.url)\n      yield put({type: \"FETCH_SUCCEEDED\", data})\n   } catch (error) {\n      yield put({type: \"FETCH_FAILED\", error})\n   }\n}\n```\n\nUsing Effects below, tasks are launched using the `fetchData` generator function.\n\n```\nimport { takeEvery } from 'redux-saga/effects'\n\nfunction* watchFetchData() {\n  yield takeEvery('FETCH_REQUESTED', fetchData)\n}\n```\n\n**`takeEvery` Effect**\n\n- allows multiple instances to be started concurrently\n- at any given moment, we can start a new task while there are still one or more previous task which have NOT yet terminated\n\n**`takeLatest` Effect**\n\n- allows only the one task to run at any moment, the latest one\n- any previous tasks still running when a new task emerges is automatically cancelled\n\n### Declarative Effects\n\n- Sagas are implemented using Generator functions.\n- To express the Saga logic. we yield plain JS objects from the Generator.\n- We call those objects **Effects**\n- An Effect is an object that contains information to be interpreted by the middleware.\n- Effects are like instructions for the middleware to perform some operation like invoke some async function or dispatch an action to the store, etc\n- To create Effects, functions are provided by the package `redux-saga/effects`\n\n**`call` Effect**\nUseful so that instead of getting a Promise back from an API call for example, we get a plain JS object representing the instruction, in this example, an object describing the function call to be performed by the middleware. [see the `Testing the saga` section above for details on this]\n\n```\nimport { call } from 'redux-saga/effects'\nimport Api from '...'\n\nconst iterator = fetchProducts()\n\n// expects a call instruction\nassert.deepEqual(\n  iterator.next().value,\n  call(Api.fetch, '/products'),\n  \"fetchProducts should yield an Effect call(Api.fetch, './products')\"\n)\n```\n\n---\n\n# Getting Started with Create React App\n\nThis project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n## Available Scripts\n\nIn the project directory, you can run:\n\n### `yarn start`\n\nRuns the app in the development mode.\\\nOpen [http://localhost:3000](http://localhost:3000) to view it in the browser.\n\nThe page will reload if you make edits.\\\nYou will also see any lint errors in the console.\n\n### `yarn test`\n\nLaunches the test runner in the interactive watch mode.\\\nSee the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.\n\n### `yarn build`\n\nBuilds the app for production to the `build` folder.\\\nIt correctly bundles React in production mode and optimizes the build for the best performance.\n\nThe build is minified and the filenames include the hashes.\\\nYour app is ready to be deployed!\n\nSee the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.\n\n### `yarn eject`\n\n**Note: this is a one-way operation. Once you `eject`, you can’t go back!**\n\nIf you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.\n\nInstead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.\n\nYou don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.\n\n## Learn More\n\nYou can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).\n\nTo learn React, check out the [React documentation](https://reactjs.org/).\n\n### Code Splitting\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)\n\n### Analyzing the Bundle Size\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)\n\n### Making a Progressive Web App\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)\n\n### Advanced Configuration\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)\n\n### Deployment\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)\n\n### `yarn build` fails to minify\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboostup%2Fredux-saga-playground","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fboostup%2Fredux-saga-playground","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fboostup%2Fredux-saga-playground/lists"}