Add a note or caution to the documentation for the `customRef` function.
demershov opened this issue · comments
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:
- Declare a variable using the
customRef
in a parent component. - Pass this variable as prop for the child component.
- Change another sibling component in the parent component that is not associated with this variable.
- The parent component is re-rendered, but since the variable declared using
customRef
is tracks by the parent component render effect, theget()
in thecustomRef
is called again, and the getter, in turn, returns a new object but with the same shape. - 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.
- 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 aset()
of thecustomRef
.
Link to the Playground
I've also created an issue at vueuse/vueuse#3992 as the useRouteQuery
also suffers from this behavior.
Thanks.
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.
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.