Automatic Dependency Tracking
This section explains how VuReact analyzes reactive dependencies in Vue 3 and generates precise dependency arrays for React Hooks.
Assumptions
To keep the examples focused and easy to follow:
- Vue and React snippets only include core logic, omitting full component wrappers and unrelated configuration
- Readers are expected to be familiar with reactivity and dependency tracking in both Vue and React
Automatic Dependency Analysis → Hook Dependency Arrays
The VuReact compiler includes a built-in automatic dependency analysis system.
It follows React’s rules to analyze reactive access within top-level arrow functions and top-level variable declarations, and generates accurate dependency arrays.
- Vue:
const count = ref(0);
const foo = ref(0);
const state = reactive({ foo: 'bar', bar: { c: 1 } });
const fn1 = () => {
count.value += state.bar.c;
console.log(count.value);
};
const fn = () => {};
const fn2 = () => {
const c = foo.value;
fn();
const fn4 = () => {
state.bar.c--;
c + count.value;
};
};
const fn3 = () => {
foo.value++;
const state = ref('fake'); // ⚠ invalid pattern
const count = state.value + 'yoxi';
count.charAt(1);
};- Compiled React (VuReact):
const count = useVRef(0);
const foo = useVRef(0);
const state = useReactive({ foo: 'bar', bar: { c: 1 } });
const fn1 = useCallback(() => {
count.value += state.bar.c;
console.log(count.value);
}, [count.value, state.bar?.c]);
const fn = () => {};
const fn2 = useCallback(() => {
const c = foo.value;
fn();
const fn4 = () => {
state.bar.c--;
c + count.value;
};
}, [foo.value, state.bar?.c, count.value]);
const fn3 = useCallback(() => {
foo.value++;
const state = useVRef('fake'); // ⚠ invalid pattern
const count = state.value + 'yoxi';
count.charAt(1);
}, [foo.value]);This comparison shows:
fn1is recognized as a top-level function, collectingcount.valueandstate.bar.cfn2traces the origin ofcand ignores the local functionfn4fn3ignores reactive variables created inside the function, and only tracks external dependencyfoo.value
Composite Access & Alias Tracing
VuReact can also trace complex alias chains and destructured values back to their reactive sources.
- Vue:
const objRef = ref({ a: 1, b: { c: 1 } });
const listRef = ref([1, 2, 3]);
const aliasA = state.foo;
const aliasB = aliasA;
const aliasC = aliasB;
const { foo: stateFoo } = state;
const [first] = listRef.value;
const traceFn = () => {
aliasC;
};
const destructureFn = () => {
stateFoo;
first;
};- Compiled React (VuReact):
const objRef = useVRef({ a: 1, b: { c: 1 } });
const listRef = useVRef([1, 2, 3]);
const aliasA = useMemo(() => state.foo, [state.foo]);
const aliasB = useMemo(() => aliasA, [aliasA]);
const aliasC = useMemo(() => aliasB, [aliasB]);
const { foo: stateFoo } = useMemo(() => state, [state]);
const [first] = useMemo(() => listRef.value, [listRef.value]);
const traceFn = useCallback(() => {
aliasC;
}, [aliasC]);
const destructureFn = useCallback(() => {
stateFoo;
first;
}, [stateFoo, first]);This demonstrates:
- Alias chains are traced step by step back to their original reactive source
- Destructured values are converted via
useMemointo trackable dependencies
Top-Level Variables → React useMemo Dependency Arrays
- Vue:
const fooRef = ref(0);
const reactiveState = reactive({ foo: 'bar', bar: { c: 1 } });
const memoizedObj = {
title: 'test',
bar: fooRef.value,
add: () => {
reactiveState.bar.c++;
},
};
let staticObj = {
foo: 1,
state: { bar: { c: 1 } },
};
const reactiveList = [fooRef.value, 1, 2];
const mixedList = [
{ name: reactiveState.foo, age: fooRef.value },
{ name: 'A', age: 20 },
];
const nestedObj = {
a: {
b: {
c: reactiveList[0],
d: () => {
return memoizedObj.bar;
},
},
e: mixedList,
},
};- Compiled React (VuReact):
const memoizedObj = useMemo(
() => ({
title: 'test',
bar: fooRef.value,
add: () => {
reactiveState.bar.c++;
},
}),
[fooRef.value, reactiveState.bar?.c],
);
let staticObj = {
foo: 1,
state: {
bar: {
c: 1,
},
},
};
const reactiveList = useMemo(() => [fooRef.value, 1, 2], [fooRef.value]);
const mixedList = useMemo(
() => [
{ name: reactiveState.foo, age: fooRef.value },
{ name: 'A', age: 20 },
],
[reactiveState.foo, fooRef.value],
);
const nestedObj = useMemo(
() => ({
a: {
b: {
c: reactiveList[0],
d: () => {
return memoizedObj.bar;
},
},
e: mixedList,
},
}),
[reactiveList[0], memoizedObj.bar, mixedList],
);Key takeaways:
memoizedObjcollects dependencies from both object fields and methodsstaticObjremains unchanged since it has no reactive accessreactiveList,mixedList, andnestedObjrecursively collect dependencies based on structure
Three Core Principles of Dependency Analysis
Only top-level optimizable expressions are analyzed Local functions and nested scopes are excluded from automatic Hook optimization
Follows React dependency rules Only external reactive access is tracked—not internal local variables
Avoids over-optimization Expressions without external reactive dependencies are not forced into Hooks
Why This Matters
In React, function components recreate top-level functions and variables on every render. If these expressions depend on reactive state without proper stabilization, it can lead to:
- Unnecessary re-renders of child components
- Frequent recomputation of Hooks
- Unstable callbacks and unpredictable performance
VuReact solves this at compile time by generating accurate dependency arrays, preserving Vue’s simplicity while ensuring React-level performance guarantees.
