Skip to content
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

Closed
andreawyss opened this issue Jan 13, 2017 · 16 comments
Closed

Discussion: Use redux-logic instead of redux-saga? #1447

andreawyss opened this issue Jan 13, 2017 · 16 comments

Comments

@andreawyss
Copy link

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?

@gihrig
Copy link
Contributor

gihrig commented Jan 15, 2017

What do you think about switching to it?

Well, it's still early days for redux-logic. We'll have to wait for a while and see where it goes. I would not consider switching until it has proven itself more.

If this discussion generates enough interest and someone submits a PR, which members of the community then find to be a better solution than redux-saga. Then it would be considered (that's my opinion, what I think).

The concepts from the project page look interesting, but the proof is always in the implementation.

It seems the developer of redux-logic is working on integrating his project into a fork of react-boilerplate. I'd be interested to hear about people's experience with that project, particularly if they have previous experience with react-boilerplate using readux-saga.

Ref: #1315 "Daemon Sagas"
Ref: #1384 "Remove per-route sagas"
Ref: #1420 "Alternative Saga Lifecycle".

@jeffbski
Copy link

jeffbski commented Jan 15, 2017

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).

@jeffbski
Copy link

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.

@kopax
Copy link

kopax commented Jan 17, 2017

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 async keyword but I wasn't able to add it to the transpilation so I didn't make it work, I used promise style instead

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 redux-saga from my boilerplate and replaced it with redux-logic

Can't wait to read others opinions.

@mxstbr
Copy link
Member

mxstbr commented Jan 21, 2017

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 mxstbr closed this as completed Jan 21, 2017
@jeffbski
Copy link

@mxstbr Thanks for the kind words. I appreciate it.

@seanlindo
Copy link

seanlindo commented Feb 9, 2017

@jeffbski Thank you for creating such a great library! I posted this question last night before finding redux-logic this morning. I've already gone as far as integrating it into react-boilerplate :)

@kopax I didn't have any trouble with the async/await keywords. I don't have a ton of business logic or calculations, but even so, this has given me the freedom to write completely pure reducers and actions-- and that feels really nice.

@jeffbski
Copy link

jeffbski commented Feb 9, 2017

@seanlindo That is fantastic! I appreciate the feedback. I too love writing pure reducers and actions.

@kopax
Copy link

kopax commented Apr 17, 2017

Could we reopen this ?

redux-saga is just a fun feature. redux-logic is designed for writing applications. I don't understand why this wasn't integrated at the first place. @mxstbr did you tried the lib? What did you think?

I've been using redux-logic for a while and honestly it beat redux-saga 10 to 0.

@gihrig
Copy link
Contributor

gihrig commented Apr 17, 2017

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.

@gihrig
Copy link
Contributor

gihrig commented Apr 19, 2017

What redux-logic users are saying.

@ms88privat
Copy link

@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 ~)

@gihrig
Copy link
Contributor

gihrig commented Jul 11, 2017

@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

fat action creators
reducers
thunks
sagas - redux-saga
epics - redux-observable
effects - redux-loop
custom middleware
redux-logic - a new approach

I would be interested to hear @jeffbski's perspective on Declarative vs Imperative
as it applies to redux-logic vs redux-saga.

@martinkadlec0
Copy link

martinkadlec0 commented Aug 20, 2017

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.

@ms88privat
Copy link

@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 cancel case)? But I have to try it to really get into it. But maybe none of these projects belong into this boilerplate or we can choose upon start.

@lock
Copy link

lock bot commented May 29, 2018

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.

@lock lock bot locked as resolved and limited conversation to collaborators May 29, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants