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

Add a note or caution to the documentation for the customRef function. #2884

Closed
demershov opened this issue May 28, 2024 · 2 comments · Fixed by #2899
Closed

Add a note or caution to the documentation for the customRef function. #2884

demershov opened this issue May 28, 2024 · 2 comments · Fixed by #2899

Comments

@demershov
Copy link

demershov commented May 28, 2024

I encountered an issue with the customRef function, where it wasn't working as i expected.
First of all, I would like to note that I have already discussed this issue with @LinusBorg and @skirtles-code in the community chat. I've come to the conclusion that although this is the expected behavior for customRef, it would be beneficial to add additional information to the documentation for other developers like me.

If a get() method inside the customRef returns an object that is the same as the previous value but is newly generated, then components that accept this customRef as a prop will perceive the new object as modified value. It is important to note that the trigger will not happen inside the set() method.

Steps to reproduce:

  1. Declare a variable using the customRef in a parent component.
  2. Pass this variable as prop for the child component.
  3. Change another sibling component in the parent component that is not associated with this variable.
  4. The parent component is re-rendered, but since the variable declared using customRef is tracks by the parent component render effect, the get() in the customRef is called again, and the getter, in turn, returns a new object but with the same shape.
  5. For the child component, this will be a new object and the child component will be re-rendered, even though the props haven't been explicitly changed.
  6. If will add a watcher for this variable inside the parent component, then the watcher will not trigger a callback, because the trigger function was not called inside a set() of the customRef.

Link to the Playground

I've also created an issue at vueuse/vueuse#3992 as the useRouteQuery also suffers from this behavior.

Thanks.

@seeniOlabode
Copy link
Contributor

Hello, thank you for logging this.

I have tested this in the Playground and confirmed the unexpected behavior. Here’s a breakdown of what happens:

  • Change Unrelated State: An unrelated state change in the parent component triggers its render function.
  • Component Re-render: This causes the parent component to re-render, which includes re-evaluating Comp.vue.
  • Evaluate CustomRef: The customRef's get() method is called again, returning a new object with the same structure as the previous value.
  • Prop Comparison: During re-rendering, Vue compares the new and previous props of the child component.
  • Perceived Prop Change: Although the objects are structurally identical, they are different instances. Vue perceives this as a prop change.
  • Child Component Re-render: This perceived prop change triggers the child component to re-render and its watchers to react, despite no actual change in the logical state.
  • This behavior occurs because the customRef does not trigger updates inside its set() method, leading to reactivity issues when the getter returns a new object instance.

Let me know if this is the case per your deductions.

@demershov
Copy link
Author

demershov commented Jun 13, 2024

Hello @seeniOlabode
Yes, it seems that this is the case.

If you or someone takes a closer look, you will understand why this happens. However, I believe it would be beneficial to include this information in the documentation.

Thank you for your attention.

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 a pull request may close this issue.

2 participants