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

Avoid capturing ComponentDef in a closure for a long time #83

Merged
merged 4 commits into from
Jul 17, 2024

Conversation

fsoikin
Copy link
Contributor

@fsoikin fsoikin commented Jul 17, 2024

The ComponentDef is captured in closures defined under where in bindComponent. In most cases this is fine, because ComponentDef is constant anyway. And even if not, bindComponent is called on every render, which means a new, fresh value of ComponentDef is re-captured on every render.

But what if such closure could survive several renders and several changes of ComponentDef? If the closure is a dispatch function, the next message that is issued through it will call a stale version of update. This is particularly important now that we added subscriptions, which keep the initial value of dispatch pretty much indefinitely.

See the new test I added in this PR to see how this could happen. If the changes to bindComponent are undone, that test fails in exactly the way the comment in it predicts.

To fix this, I am now keeping ComponentDef as a prop on the component, and always retrieving it from the component props when needed, rather than capturing it in a closure. This way, whenever the ComponentDef is required, it is always the one from the latest render.

There will be another fix in purescript-hooks to take advantage of this fix to fix the next level of the same problem.

@fsoikin fsoikin requested a review from a team July 17, 2024 02:04
Copy link
Contributor

@gasi gasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — nice find 👍


- Fixed a bug that allowed `ComponentDef` to be captured in closures for a long
time, which could lead to using stale values in complex scenarios where
`ComponentDef` is not constant, but depends on arguments. See [#83](https://github.com/collegevine/purescript-elmish/pull/83).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Within the same repo, this syntax will autolink AFAIK 👍

Suggested change
`ComponentDef` is not constant, but depends on arguments. See [#83](https://github.com/collegevine/purescript-elmish/pull/83).
`ComponentDef` is not constant, but depends on arguments. See #83.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't. I tried this first.

@fsoikin fsoikin merged commit b43eb3c into master Jul 17, 2024
4 checks passed
@fsoikin fsoikin deleted the fix-closure-bug branch July 17, 2024 12:57
@@ -61,6 +63,35 @@ spec = describe "Elmish.Component.wrapWithLocalState" do
findAll ".t--counter" <#> length >>= shouldEqual 2
findAll ".t--counter" >>= forEach (nearestEnclosingReactComponentName >>= shouldEqual "Elmish_Counter")

it "calls the correct closure of `update` when dispatching events" $
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻

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

Successfully merging this pull request may close these issues.

3 participants