Reactive Effect Tracking in Vue (TypeScript)
Effect tracking is a core concept in reactive frameworks like Vue. This challenge asks you to implement a simplified version of effect tracking, focusing on how changes to reactive data trigger updates to dependent effects. Successfully completing this challenge will give you a deeper understanding of how reactivity works under the hood.
Problem Description
You are tasked with creating a basic effect tracking system. This system should allow you to mark functions as "effects" that depend on specific reactive values. When these reactive values change, the registered effects should be automatically re-executed.
What needs to be achieved:
- Reactive Values: Create a function
reactivethat takes a plain JavaScript object and returns a reactive version of it. Changes to properties of the reactive object should trigger effect re-runs. - Effect Registration: Create a function
effectthat takes a function as an argument. This function represents an effect. Theeffectfunction should:- Automatically execute the provided function once when it's registered.
- Track all reactive values accessed within the function.
- Re-execute the function whenever any of the tracked reactive values change.
- Change Detection: The
reactivefunction should trigger effect re-runs when a property is set on the reactive object.
Key Requirements:
- The solution must be written in TypeScript.
- The
reactiveandeffectfunctions should be clearly defined and documented. - The effect tracking should be robust and handle multiple effects depending on the same reactive values.
- Avoid using Vue's built-in reactivity system. This is about implementing a simplified version.
Expected Behavior:
- When
effectis called with a function, the function should execute immediately. - If the function accesses reactive properties, those properties should be tracked.
- When a tracked reactive property is modified, all effects that depend on that property should be re-executed.
- Effects should be re-executed in a predictable order (e.g., the order they were registered).
Edge Cases to Consider:
- Effects accessing the same reactive property multiple times.
- Nested effects (an effect calling another effect).
- Effects modifying reactive properties (this should trigger infinite loops, which you don't need to prevent, but should be aware of).
- Effects that don't access any reactive properties (should still be registered but not re-executed).
Examples
Example 1:
const state = reactive({ count: 0 });
const effect1 = effect(() => {
console.log("Effect 1: Count is", state.count);
});
state.count++; // Output: Effect 1: Count is 1
state.count++; // Output: Effect 1: Count is 2
Explanation: effect1 is registered and immediately executed. When state.count is incremented, effect1 is re-executed.
Example 2:
const state = reactive({ a: 1, b: 2 });
const effect1 = effect(() => {
console.log("Effect 1: a is", state.a);
});
const effect2 = effect(() => {
console.log("Effect 2: b is", state.b);
});
state.a++; // Output: Effect 1: a is 2
state.b++; // Output: Effect 2: b is 3
Explanation: Both effect1 and effect2 are registered and immediately executed. Changing state.a triggers effect1, and changing state.b triggers effect2.
Example 3: (Edge Case - Multiple accesses to same property)
const state = reactive({ value: 10 });
const effect = effect(() => {
console.log("Effect: Value is", state.value, "x 2 =", state.value * 2);
});
state.value = 20; // Output: Effect: Value is 20 x 2 = 40
Explanation: The effect accesses state.value twice. A single change to state.value should still trigger the effect only once.
Constraints
- Time Complexity: The effect re-execution should be reasonably efficient. Avoid unnecessary overhead.
- Space Complexity: The system should not consume excessive memory, especially with a large number of reactive values and effects.
- Input Type: The
reactivefunction should accept a plain JavaScript object (no arrays, functions, or other complex types initially). - No External Libraries: Do not use any external libraries or Vue's built-in reactivity system. This is about implementing the core logic.
Notes
- Consider using a
MaporSetto track reactive dependencies. - Think about how to efficiently determine which effects need to be re-executed when a reactive value changes.
- The order of effect execution is important. Try to maintain a consistent order.
- This is a simplified implementation. Real-world reactivity systems are significantly more complex, handling things like asynchronous updates, batching, and more. Focus on the core concepts of dependency tracking and effect re-execution.
- Start with a small, focused example and gradually add complexity. Testing is crucial.