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

Forbid imperative calls of effector units from reducers #541

Closed
AlexandrHoroshih opened this issue Oct 27, 2021 · 9 comments
Closed

Forbid imperative calls of effector units from reducers #541

AlexandrHoroshih opened this issue Oct 27, 2021 · 9 comments
Assignees
Labels
core effector package RFC Request for Comments

Comments

@AlexandrHoroshih
Copy link
Member

AlexandrHoroshih commented Oct 27, 2021

Proposal

Throw error or show a console warning, if event or effect have been manually called from inside of store reducer or other pure functions.

Currently effector does not forbid or warn about event or effect calls inside reducers or other supposed-to-be-pure functions like fn of sample or combine rule

But, as it can be seen at effector telegram chat, many people do call events from reducers sometimes (and sometimes for a really long time) and have no clue, that it is not how it supposed to work.

Even though effector, apparently, still can handle that, using events this way goes way too much aganist declared rules and some projects may found themselves absolutely incompatible with some future release of effector

I think, explicit warning (or even exception) like

effector events must not be called from pure functions like store reducers or fn of sample.
Use effector operators instead.
Or, if you really need imperative control flow, use watch or effect for that

will help a lot here

Use case

Today an effector user shared the way he has been using .on for a long time - it was something like this:

// this code must be either rewritten to operators or written in the same way, but inside `.watch`
$store.on(someEvent, (state, payload) => { 
 // store reducer meant to update stored value
 if (payload.some) {
   // but there is some control flow happening instead, which is wrong
   someEffect(state)
 } else {
  someOtherEvent(payload)
 }
 // next state is never returned - plain wrong usage of `.on`
})

From his words, he was using effector like that from the very beginning and have not seen any problems yet.

Obviously, all of it mentioned in the documentation, but it looks like that sometimes people do not read it carefully enough and little push is needed

@AlexandrHoroshih AlexandrHoroshih added the RFC Request for Comments label Oct 27, 2021
@AlexandrHoroshih
Copy link
Member Author

I guess, it can be implemented in the same way scopes work 🤔

let pureContext = false
const runPure = (cb, ...args) => {
 pureContext = true
 cb(...args)
 pureContext = false
} 

...
// somewhere in `launch`
if (pureContext) {
 throw Error(...)
}

@Kelin2025
Copy link
Member

Should we really add this to core package? I think it can be implemented through effector/eslint-plugin

@AlexandrHoroshih
Copy link
Member Author

AlexandrHoroshih commented Oct 27, 2021

Should we really add this to core package? I think it can be implemented through effector/eslint-plugin

Sure, it can (and, i guess, should) be implemented as eslint-plugin rule too, but it won't be as reliable as actual check at the core + not all projects using this eslint-plugin

So, i think, it is better if plain-wrong practices like in the example are prevented at the core level

@zerobias
Copy link
Member

There may be some edge cases as event calls may be deeply reordered before execution, not even mention events called from watchers during execution of the forbidden event call, so runtime error may appear in entirely irrelevant places of code
And avoiding that issue would introduce another full-featured call stack with complex logic to manage it

I would rather prefer an eslint rule

@sergeysova
Copy link
Member

sergeysova commented Nov 26, 2021

@zerobias What about to add some extra parameter to a compute

And in the appropriate switch/case check it only if parameter is true? Computations already has isWatch flag, why not add isReducer flag?

it could shoot me in the foot?

@zerobias
Copy link
Member

zerobias commented Dec 7, 2021

@sergeysova well, looks like it should work in that way, let's try 👌

@zerobias zerobias self-assigned this Dec 7, 2021
@zerobias zerobias added the core effector package label Dec 7, 2021
@zerobias zerobias added this to the effector 22.2.0 milestone Dec 7, 2021
zerobias added a commit that referenced this issue Dec 7, 2021
also add forgotten third argument to step functions

related issue #541
zerobias added a commit that referenced this issue Dec 7, 2021
related issue #541

this commit should fail
zerobias added a commit that referenced this issue Dec 7, 2021
@zerobias
Copy link
Member

zerobias commented Dec 8, 2021

Deprecation warning will be published in the next minor release

@zerobias zerobias closed this as completed Dec 8, 2021
@RemoteRacoon
Copy link

Proposal

Throw error or show a console warning, if event or effect have been manually called from inside of store reducer or other pure functions.

Currently effector does not forbid or warn about event or effect calls inside reducers or other supposed-to-be-pure functions like fn of sample or combine rule

But, as it can be seen at effector telegram chat, many people do call events from reducers sometimes (and sometimes for a really long time) and have no clue, that it is not how it supposed to work.

Even though effector, apparently, still can handle that, using events this way goes way too much aganist declared rules and some projects may found themselves absolutely incompatible with some future release of effector

I think, explicit warning (or even exception) like

effector events must not be called from pure functions like store reducers or fn of sample.
Use effector operators instead.
Or, if you really need imperative control flow, use watch or effect for that

will help a lot here

Use case

Today an effector user shared the way he has been using .on for a long time - it was something like this:

// this code must be either rewritten to operators or written in the same way, but inside `.watch`
$store.on(someEvent, (state, payload) => { 
 // store reducer meant to update stored value
 if (payload.some) {
   // but there is some control flow happening instead, which is wrong
   someEffect(state)
 } else {
  someOtherEvent(payload)
 }
 // next state is never returned - plain wrong usage of `.on`
})

From his words, he was using effector like that from the very beginning and have not seen any problems yet.

Obviously, all of it mentioned in the documentation, but it looks like that sometimes people do not read it carefully enough and little push is needed

But what if I want to create an effect which triggers 2 effects which are expected to be done together?

const effect = createEffect(() => Promise.all([fx1, fx2]))

If we write this way we get the warning in console as well

@zerobias
Copy link
Member

zerobias commented Jul 1, 2023

There should be no warning in your case, it’s valid usage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core effector package RFC Request for Comments
Projects
None yet
Development

No branches or pull requests

5 participants