-
Notifications
You must be signed in to change notification settings - Fork 6.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion: Use redux-logic instead of redux-saga? #1447
Comments
Well, it's still early days for If this discussion generates enough interest and someone submits a PR, which members of the community then find to be a better solution than The concepts from the project page look interesting, but the proof is always in the implementation. It seems the developer of Ref: #1315 "Daemon Sagas" |
While searching for the best way to create business logic with Redux, I ended up not being completely happy with any of the common choices (fat action creators, thunks, sagas, epics (redux-observable)) which I chronicled here. So I ended up building redux-logic about 5 months ago and have been very happy with the results. It is a young project but I'm getting really good feedback from people that have tried it. Most people report that their code is greatly simplified and more understandable than their previous versions. Other than the ongoing exercise of adding more docs and examples and adding TypeScript support, there's nothing major that is outstanding, things have been stable. My main goal was to make it simpler to implement business logic while still having some great built-in features like cancellation, latest filtering, debouncing, and throttling. So I made those declarable and then exposed an API where you can write your business logic for both intercepting (validating/transforming/augmenting) actions as well as async processing. The nice thing is that you can use the style of JS that you like for your team, you don't have to be proficient in generators or observables. You can use a style you are used to whether that is: callbacks, promises, async/await, or observables. Under the covers I implement the declarable features using observables but those are simply enabled by setting properties on the logic, the redux-logic developer writes their business logic code in whatever JS style their team is most comfortable with. I have a variety of examples in the readme, the examples folder, and linked in jsfiddle from the redux-logic project, but if anyone has any specific use case they would like me to create, just let me know and I'll add it. I think the auto-search examples are pretty neat since they show how easy it is to do cancellation, latest filtering, and debouncing while using promises or async/await for fetching with just a small amount of logic code. Besides being able to write code in a variety of ways (not requiring generators or observables), redux-logic also allows you to intercept actions like you would do with custom middleware, so that allows easy validation, transformation, augmentation, etc. allowing you to keep all of your business logic in one place. So I can keep you posted on my progress with the fork and I am happy to answer any questions or concerns. For those curious how the code compares to sagas, the built-in react-boilerplate HomePage saga rewritten with logic (using a promise style code). |
Regarding #1470 accessing a value in store with app/utils/request.js if done with redux-logic, I would just write a function augmentOptions(options, state) which gets the value from the store state and merges the appropriate request option (header, SSL key, etc), then use that in the logic to augment the options before calling request. const logic = createLogic({
type: 'FOO',
process({ getState, action }) {
const state = getState();
request(url, augmentOptions({}, getState()))
.then(result => fooSuccess(result));
}
}); Alternatively if you use actions to pass around the URLs to fetch along with any associated options, then you could use a redux-logic transformer to add options on the fly to the action. For instance if you use an action like this for all your fetches: {
type: FETCH_URL,
url: 'http://google.com',
options: {
headers: {
cat: 'dog'
}
},
successActionType: 'LOAD_REPOS_SUCCESS'
} then you can implement a transform logic that merges in whatever variable from the store, you will just want this logic to be defined earlier before the one that makes fetches. So you would probably define this to load in your createLogicMiddleware so that it loads before the the fetchUrlLogic. For instance: In your app/store.js createStore function: import coreLogic from './coreLogic';
const injectedDeps = { requestUtil }; // can inject helpers available to all logic
const logicMiddleware = createLogicMiddlware(coreLogic, injectedDeps); In my coreLogic.js import { secretKeySelector } from './selectors';
import fp from 'lodash/fp';
export const addAuthHeaderToFetchActionsLogic = createLogic({
type: 'FETCH_URL',
transform({ getState, action }, next) {
const secret = secretKeySelector()(getState()); // access the secret
const augmentedAction = fp.set('options.headers.AUTH_KEY', secret, action);
next(augmentedAction); // we've replaced the action going down
}
});
export const fetchUrlLogic = createLogic({
type: 'FETCH_URL',
process({ getState, action, requestUtil}) {
const { url, options, successActionType } = action;
return requestUtil(url, options)
.then(result => { type: successActionType, payload: result });
}
});
export default [
// needs to come before fetchUrlLogic
addAuthHeaderToFetchActionsLogic,
fetchUrlLogic
]; So basically addAuthHeaderToFetchActionsLogic will intercept and add in the secret from the store to the options in the action, then the fetchUrlLogic just does fetching. In this user's case all fetches are augmented, but you could also perform selective augmentation based on the URL domain or the user, etc. You could also have just done this all in the fetchUrlLogic unless there was a reason to break it out. But at least it is a nice exercise to see how augmenting (transforming) actions works with redux-logic. |
Personnaly, I am interested. I don't see redux-sagas as a good friend, it doesn't cover all the scope it should, using generator makes it even more complicated. Some basic stuff requires lines of code. @gihrig I have tested the project and try to port my project on redux-logic. Reason: It's easier to explain and there is less chance to do mistake. I have tried to play with the Edit: After some further testing, it appeart the code is much less complex to write and I have less bugs than before. I definitly removed Can't wait to read others opinions. |
I doubt we'll switch to it at this early stage of the project, but it certainly looks intriguing! Will have to play around with it. Thanks for this awesome discussion! |
@mxstbr Thanks for the kind words. I appreciate it. |
@jeffbski Thank you for creating such a great library! I posted this question last night before finding @kopax I didn't have any trouble with the |
@seanlindo That is fantastic! I appreciate the feedback. I too love writing pure reducers and actions. |
Could we reopen this ?
I've been using |
I agree, despite my original post in this issue. For anyone who's interested in what react-boilerplate would be like with redux-logic in place of redux-saga, it's been done. There's a fork of react-boilerplate 3.4 with redux logic in place of redux-saga. |
What redux-logic users are saying. |
@gihrig I worked quite a lot with redux-saga and now heard about redux-logic. Maybe I didn't get the whole picture yet, but for me both libraries are solving different problems. Redux-logic looks like it provides a simple API and configurations for archiving common side effects. So basically the developer has just to learn how to configure it and then he is good to go to write fast business logic. The problem I see here is, that you can not solve problems that redux-logic isn't aware of. With redux-saga it is quite the opposite. You have to build almost every functionality by yourself, but you are not limited to any kind. If this is true, then I believe redux-saga belongs to this boilerplate, because this repository isn't about getting shit done (create-react-app), it is about creating the most professional apps. (as stated in the readme ~) |
@ms88privat Your argument mirrors the general Declarative vs Imperative argument. It's one I have come down on both sides of in different situations. Declarative is great when it gets the job done, for everything else you need an Imperative API. So, potentially, I can agree with your argument. I'm wondering though, is your argument based on a practical example, or more theoretical? Did you read @jeffbski's detailed walkthrough on why redux-logic exists? Here, he covers
I would be interested to hear @jeffbski's perspective on Declarative vs Imperative |
Personally, I am not big fan of redux-logic, I found that trying to figure out what some code does - especially if written by someone else - takes me way longer than with sagas which are generally quite straight forward. The ability to intercept actions is at least interesting but I have yet to stumble upon a use case in any of my projects. In conclusion, I'd prefer if react-boilerplate would stick with redux-saga for now. |
@gihrig I read this walkthrough, but it didn't tell me anything new. I used redux-saga heavily but never redux-logic. I think it will break down when you try to combine multiple actions into one workflow (besides the standard |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Please take a look at redux-logic.
It has all the features of redux-saga and more with a much simpler declarative syntax.
What do you think about switching to it?
The text was updated successfully, but these errors were encountered: