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

State change within reactive handler requires wrapping in tick() #5848

Closed
arackaf opened this issue Jan 2, 2021 · 3 comments
Closed

State change within reactive handler requires wrapping in tick() #5848

arackaf opened this issue Jan 2, 2021 · 3 comments

Comments

@arackaf
Copy link
Contributor

arackaf commented Jan 2, 2021

Is this about svelte@next? This project is currently in a pre-release stage and breaking changes may occur at any time. Please do not post any kind of bug reports or questions on GitHub about it.

No

Describe the bug
A clear and concise description of what the bug is.

A state change that's inside of a reactive handler requires wrapping in tick() for the state change to register. REPL below.

Logs
Please include browser console and server logs around the time this bug occurred.

N/A

To Reproduce
To help us help you, if you've found a bug please consider the following:

https://svelte.dev/repl/b4ec7663c4d44085a754160863cb611c?version=3.31.1

Basically, toggle disabled on, then off. You should see an activated message, but won't. To fix, go to the Thing component, and comment out line 31, and uncomment 34 (which has the call to tick())

Occasionally, this won't be possible, and that's fine – we still appreciate you raising the issue. But please understand that Svelte is run by unpaid volunteers in their free time, and issues that follow these instructions will get fixed faster.

Expected behavior
A clear and concise description of what you expected to happen.

Should work

Stacktraces
If you have a stack trace to include, we recommend putting inside a <details> block for the sake of the thread's readability:

N/A

Stack trace

Stack trace goes here...

Information about your Svelte project:
To make your life easier, just run npx envinfo --system --npmPackages svelte,rollup,webpack --binaries --browsers and paste the output here.

  • Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, IE 10)
    Chrome 87

  • Your operating system: (e.x. OS X 10, Ubuntu Linux 19.10, Windows XP, etc)

Mac OS

  • Svelte version (Please check you can reproduce the issue with the latest release!)

Latest in REPL

  • Whether your project uses Webpack or Rollup

REPL

Severity
How severe an issue is this bug to you? Is this annoying, blocking some users, blocking an upgrade or blocking your usage of Svelte entirely?

Blocking to anyone who wouldn't think to use tick()

Note: the more honest and specific you are here the more we will take you seriously.

Additional context
Add any other context about the problem here.

@Conduitry
Copy link
Member

I believe this has come up before in an issue, although I can't find it right now.

Reactive blocks are sorted according to which variables are used within the blocks and which are assigned to within the blocks, falling back to the order that the blocks are written in the component. These reactive blocks are then run in order whenever there are updates that need to happen. Any changes that happen synchronously while running these reactive blocks will not trigger another wave of updates after this one finishes, but this is normally not a problem if the compiler is able to sort the reactive blocks correctly. For example, if one block updates foo and another uses foo, the block that updates foo will end up before the other one in the compiled code, even if it were second in the component definition.

In your component, the compiler cannot tell that the second reactive block might cause changes the should cause the first block to be re-run, because the updates to activated are hidden in the activate() function. So it defaults to keeping them in the order you wrote them. activated is set to true after the first reactive block has already checked its value, and so nothing happens. Your solution of using tick() works because that pushes the activated = true into the next microtask, which will then trigger another wave of reconciling reactive blocks. Other more elegant solutions would be to write the component in such a way that the compiler can see that the second reactive block changes a variable that the first one looks at (and thus output them in the other order in the compiled component), or else simply switch the order of the two reactive blocks in your component, so that activated is already true by the time you're checking its value.

In short, this is the intended behavior. And there's a long-standing TODO to better document the nuances of the reactivity model somewhere on the site.

@arackaf
Copy link
Contributor Author

arackaf commented Jan 2, 2021

@Conduitry thanks for the explanation!

@arackaf
Copy link
Contributor Author

arackaf commented Jan 2, 2021

@Conduitry seriously that explanation was uniquely valuable. I urge you to just copy paste the relevant portions into the docs. I'm sure it'd be incredibly valuable to others.

ivan-lednev referenced this issue in ivan-lednev/obsidian-day-planner Sep 7, 2023
There are issues in svelte with cascading dependencies
in reactive declarations, from what I understand.
Since `background color` relies on `$store.timelineColored`,
having `textColor` rely on both seemed to cause issues.
Removing the direct dependency of the store works,
anyway.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants