{"action":"created","issue":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/8","labels_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/8/labels{/name}","comments_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/8/comments","events_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/8/events","html_url":"https://github.com/yelouafi/redux-saga/issues/8","id":123459831,"number":8,"title":"Sagas should rather be totally autonomous","user":{"login":"slorber","id":749374,"avatar_url":"https://avatars.githubusercontent.com/u/749374?v=3","gravatar_id":"","url":"https://api.github.com/users/slorber","html_url":"https://github.com/slorber","followers_url":"https://api.github.com/users/slorber/followers","following_url":"https://api.github.com/users/slorber/following{/other_user}","gists_url":"https://api.github.com/users/slorber/gists{/gist_id}","starred_url":"https://api.github.com/users/slorber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slorber/subscriptions","organizations_url":"https://api.github.com/users/slorber/orgs","repos_url":"https://api.github.com/users/slorber/repos","events_url":"https://api.github.com/users/slorber/events{/privacy}","received_events_url":"https://api.github.com/users/slorber/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2015-12-22T11:03:37Z","updated_at":"2015-12-27T00:20:58Z","closed_at":null,"body":"Hello,\r\n\r\nI've seen the real world where some sagas need to be stateful to know if the data needs to be fetched or not:\r\n\r\n```\r\nexport default function* root(getState) {\r\n\r\n  const getUser = login => getState().entities.users[login]\r\n  const getRepo = fullName => getState().entities.repos[fullName]\r\n  const getStarredByUser = login => getState().pagination.starredByUser[login]\r\n  const getStargazersByRepo = fullName => getState().pagination.stargazersByRepo[fullName]\r\n\r\n  yield fork(watchNavigate)\r\n  yield fork(watchLoadUserPage, getUser, getStarredByUser)\r\n  yield fork(watchLoadRepoPage, getRepo, getStargazersByRepo)\r\n  yield fork(watchLoadMoreStarred, getStarredByUser)\r\n  yield fork(watchLoadMoreStargazers, getStargazersByRepo)\r\n}\r\n\r\n// Fetches data for a User : user data + starred repos\r\nfunction* watchLoadUserPage(getUser, getStarredByUser) {\r\n  while(true) {\r\n    const {login, requiredFields = []} = yield take(actions.LOAD_USER_PAGE)\r\n\r\n    yield fork(loadUser, login, getUser(login), requiredFields)\r\n    yield fork(loadStarred, login, getStarredByUser(login))\r\n  }\r\n}\r\n\r\n// load user unless it is cached\r\nfunction* loadUser(login, user, requiredFields) {\r\n  if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {\r\n    yield call(fetchUser, login)\r\n  }\r\n}\r\n\r\n// load next page of repos starred by this user unless it is cached\r\nfunction* loadStarred(login, starredByUser = {}, loadMore) {\r\n  if (!starredByUser.pageCount || loadMore)\r\n    yield call(\r\n      fetchStarred,\r\n      login,\r\n      starredByUser.nextPageUrl || firstPageStarredUrl(login)\r\n    )\r\n}\r\n```\r\n\r\n\r\nI think we already discussed that but I think the Saga should be a totally autonomous process that listen for events and perform effects.\r\n\r\nThe problem here for me is that `getState().entities.users[login]` is actually a state that has the purpose of being displayed to the UI, as it is computed by Redux reducers. So basically you are coupling the way a Saga may perform effects to the UI state. Your saga is not really stateful, but it can use state provided by a dependency (the UI state).\r\n\r\nI think the Saga should not know anything about the UI state at all. Refactoring the layout of the UI state should not need to perform any modification to the saga logic.\r\n\r\nIn backend systems, sagas can be distributed across a cluster of machines, and the saga can't really (or efficiently) query synchronously the state of the app as it may be stored on other machines. That's why Sagas are stateful and decoupled on the backend.\r\n\r\nMaybe we should not force the user to use this decoupling as it introduces more complexity, but at least give the opportunity for the Saga to really be stateful, instead of reusing the UI state provided by getState. A simple possibility would be to register a reducer to the Saga for example.\r\n\r\n\r\nSee for example a saga implemented in Java here: http://www.axonframework.org/docs/2.0/sagas.html\r\n\r\n```java\r\npublic class OrderManagementSaga extends AbstractAnnotatedSaga {\r\n\r\n    private boolean paid = false;\r\n    private boolean delivered = false;\r\n    private transient CommandGateway commandGateway;\r\n\r\n    @StartSaga\r\n    @SagaEventHandler(associationProperty = \"orderId\")\r\n    public void handle(OrderCreatedEvent event) {\r\n        // client generated identifiers (1)\r\n        ShippingId shipmentId = createShipmentId();\r\n        InvoiceId invoiceId = createInvoiceId();\r\n        // associate the Saga with these values, before sending the commands (2)\r\n        associateWith(\"shipmentId\", shipmentId);\r\n        associateWith(\"invoiceId\", invoiceId);\r\n        // send the commands\r\n        commandGateway.send(new PrepareShippingCommand(...));\r\n        commandGateway.send(new CreateInvoiceCommand(...));\r\n    }\r\n\r\n    @SagaEventHandler(associationProperty = \"shipmentId\")\r\n    public void handle(ShippingArrivedEvent event) {\r\n        delivered = true;\r\n        if (paid) {\r\n            end(); (3)\r\n        }\r\n    }\r\n\r\n    @SagaEventHandler(associationProperty = \"invoiceId\")\r\n    public void handle(InvoicePaidEvent event) {\r\n        paid = true;\r\n        if (delivered) {\r\n            end(); (4)\r\n        }\r\n    }\r\n\r\n    // ...\r\n\r\n}\r\n```\r\n\r\nAs you can see, the OrderManagementSaga is created after every OrderCreatedEvent (so many OrderManagementSaga can live at the same time in the system, but this probably does not apply to frontend sagas). These sagas are stateful and have the `paid` and `delivered` attributes.\r\n\r\nThis is just the Saga code, but you can guess that there's in the system another item called `Shippement` that stores an attribute `delivred`.\r\n\r\nThis may seem surprising but it is not a problem if the global system stores the same data in multiple places. Each place can pick the data it needs from the events. This permits to avoid introducing new dependencies. The only real shared dependency all the components have is the event log.\r\n\r\nThe current approach of using Redux' getState() in Sagas for me is a bit similar to using `waitFor` of Flux. It works but creates coupling that can be avoided.\r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/yelouafi/redux-saga/issues/comments/167372941","html_url":"https://github.com/yelouafi/redux-saga/issues/8#issuecomment-167372941","issue_url":"https://api.github.com/repos/yelouafi/redux-saga/issues/8","id":167372941,"user":{"login":"slorber","id":749374,"avatar_url":"https://avatars.githubusercontent.com/u/749374?v=3","gravatar_id":"","url":"https://api.github.com/users/slorber","html_url":"https://github.com/slorber","followers_url":"https://api.github.com/users/slorber/followers","following_url":"https://api.github.com/users/slorber/following{/other_user}","gists_url":"https://api.github.com/users/slorber/gists{/gist_id}","starred_url":"https://api.github.com/users/slorber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/slorber/subscriptions","organizations_url":"https://api.github.com/users/slorber/orgs","repos_url":"https://api.github.com/users/slorber/repos","events_url":"https://api.github.com/users/slorber/events{/privacy}","received_events_url":"https://api.github.com/users/slorber/received_events","type":"User","site_admin":false},"created_at":"2015-12-27T00:20:58Z","updated_at":"2015-12-27T00:20:58Z","body":"@yelouafi after thinking about it a bit it seems to be a non issue because in your examples you have shawn that a saga could be living the whole app lifetime with a `while (true)`, and that it could use local variables outside of the loop as state so basically it seems to me that getState is not a requirement to implement stateful sagas\r\n\r\nLike the authenticate example: you have to know when the user is connected or disconnected to perform the appropriate effects, however it did not require any use of `getState` at all.\r\n\r\nHowever, the caching system in the \"real world\" example would probably be harder to write without getState: https://github.com/yelouafi/redux-saga/blob/master/examples/real-world/sagas/index.js\r\nI would like to see how you could handle that without getState :)\r\n"}}