Reactive Data Management with Proxies in Vue
Vue.js leverages proxies under the hood for reactivity. This challenge asks you to build a simplified reactive system using JavaScript proxies, mimicking Vue's core reactivity principles. This exercise will deepen your understanding of how Vue tracks changes and triggers updates, a fundamental concept for building dynamic user interfaces.
Problem Description
You are tasked with creating a reactive object using JavaScript proxies. This object should track changes to its properties and trigger a callback function whenever a property is accessed or modified. The reactive object should support nested properties, meaning changes to properties within nested objects should also trigger the callback.
What needs to be achieved:
- Create a function
reactivethat takes an object as input and returns a proxy object. - The proxy object should intercept property access (getting) and modification (setting).
- When a property is accessed, the callback function provided to
reactiveshould be executed with the target object and the key being accessed. - When a property is modified, the callback function should be executed with the target object, the key being modified, and the new value.
- The
reactivefunction should handle nested objects recursively, making the entire object tree reactive.
Key Requirements:
- Use the
ProxyAPI. - Implement both
getandsettraps. - Handle nested objects correctly.
- The callback function should receive the target object, the key, and the new value (for setting).
- The original object should not be modified directly; all changes should go through the proxy.
Expected Behavior:
When a property of the reactive object is accessed or modified, the provided callback function should be invoked. The callback should receive the object, the key, and the new value (if setting). Changes to nested properties should also trigger the callback.
Edge Cases to Consider:
- Deleting properties: While not strictly required, consider how you might handle property deletion.
- Setting properties to
undefined: Ensure this is handled correctly. - Modifying arrays: Arrays are objects in JavaScript, so changes to array elements should also be reactive.
- Non-object values: The
reactivefunction should gracefully handle properties with non-object values (e.g., numbers, strings, booleans).
Examples
Example 1:
Input:
const data = {
name: 'John',
age: 30,
address: {
city: 'New York',
zip: '10001'
}
};
const callback = (target, key, newValue?) => {
console.log(`Accessed or modified: ${key}`, target, newValue);
};
const reactiveData = reactive(data, callback);
reactiveData.name = 'Jane';
console.log(reactiveData.age);
reactiveData.address.city = 'Los Angeles';
Output:
Accessed or modified: name Jane { name: 'Jane', age: 30, address: { city: 'New York', zip: '10001' } } undefined
Accessed or modified: age { name: 'John', age: 30, address: { city: 'New York', zip: '10001' } } 30
Accessed or modified: city Los Angeles { city: 'Los Angeles', zip: '10001' }
Explanation: The callback is triggered when name is modified, age is accessed, and address.city is modified.
Example 2:
Input:
const data = {
count: 0
};
const callback = (target, key, newValue) => {
console.log(`Count changed to: ${newValue}`);
};
const reactiveData = reactive(data, callback);
reactiveData.count = 1;
reactiveData.count = 2;
Output:
Count changed to: 1
Count changed to: 2
Explanation: The callback is triggered each time count is modified.
Example 3: (Edge Case - Setting to undefined)
Input:
const data = {
value: 10
};
const callback = (target, key, newValue) => {
console.log(`Value set to: ${newValue}`);
};
const reactiveData = reactive(data, callback);
reactiveData.value = undefined;
Output:
Value set to: undefined
Explanation: Setting a property to undefined should also trigger the callback.
Constraints
- The
reactivefunction must accept two arguments: the object to make reactive and a callback function. - The callback function must accept three arguments: the target object, the key being accessed/modified, and the new value (if setting).
- The solution must be written in TypeScript.
- The solution should be reasonably performant. Avoid unnecessary operations within the
getandsettraps. - The original object should not be mutated directly.
Notes
- Consider using recursion to handle nested objects.
- The
gettrap should return the value of the property. - The
settrap should returntrueto indicate that the property was successfully set. - This is a simplified implementation and does not include all the features of Vue's reactivity system (e.g., dependency tracking, batch updates). The focus is on understanding the core concepts of using proxies for reactivity.
- Think about how to handle different data types within the object.