Implementing Reactive Data Handling with Vue Proxies
Vue 3's reactivity system relies heavily on JavaScript Proxies to track data changes. This challenge will guide you through implementing a simplified version of this proxy-based reactivity system, focusing on how to detect property additions, deletions, and modifications. Understanding this mechanism is crucial for building dynamic and responsive user interfaces in Vue.
Problem Description
Your task is to create a function, createReactiveObject, that takes a plain JavaScript object and returns a "reactive" version of it. This reactive object should mimic Vue's behavior by:
- Intercepting property reads: When a property is accessed on the reactive object, you should log a message indicating the property name being read.
- Intercepting property writes: When a property is modified on the reactive object, you should log a message indicating the property name being modified and its new value.
- Intercepting property additions: When a new property is added to the reactive object, you should log a message indicating the property name being added and its value.
- Intercepting property deletions: When a property is deleted from the reactive object, you should log a message indicating the property name being deleted.
You will achieve this using JavaScript's Proxy object.
Key Requirements:
- The
createReactiveObjectfunction should accept a single argument:target(a plain JavaScript object). - It should return a
Proxyobject that wraps thetarget. - The
Proxyshould implement theget,set, anddeletePropertytraps. gettrap: Log"Reading property: [propertyName]".settrap: Log"Setting property: [propertyName] to [newValue]". After logging, the value should be updated on thetargetobject.deletePropertytrap: Log"Deleting property: [propertyName]". After logging, the property should be deleted from thetargetobject.
Expected Behavior:
When properties are accessed, modified, added, or deleted on the returned reactive object, the specified console logs should appear, and the underlying target object should be updated accordingly.
Edge Cases:
- Consider how your proxy handles nested objects. While full recursive reactivity is beyond the scope of this basic challenge, ensure your traps are correctly applied to the top-level object.
- What happens when trying to set a property to
undefinedor delete a non-existent property? Your implementation should handle these gracefully.
Examples
Example 1:
const originalData = { name: 'Alice', age: 30 };
const reactiveData = createReactiveObject(originalData);
console.log(reactiveData.name); // Expected log: "Reading property: name"
console.log(reactiveData.age); // Expected log: "Reading property: age"
reactiveData.age = 31; // Expected log: "Setting property: age to 31"
reactiveData.city = 'New York'; // Expected log: "Setting property: city to New York"
delete reactiveData.name; // Expected log: "Deleting property: name"
console.log(originalData); // Expected output: { age: 31, city: 'New York' }
Explanation:
The get trap is invoked when reactiveData.name and reactiveData.age are accessed, logging the property names. The set trap is invoked when reactiveData.age is updated and reactiveData.city is added, logging the property and new value. The deleteProperty trap is invoked when reactiveData.name is deleted. The original originalData object is modified as expected.
Example 2:
const emptyObj = {};
const reactiveEmpty = createReactiveObject(emptyObj);
reactiveEmpty.value = 100; // Expected log: "Setting property: value to 100"
console.log(reactiveEmpty.value); // Expected log: "Reading property: value"
delete reactiveEmpty.value; // Expected log: "Deleting property: value"
console.log(emptyObj); // Expected output: {}
Explanation:
This example demonstrates adding and then deleting a property from an initially empty object. The corresponding set and deleteProperty traps are triggered.
Constraints
- The
createReactiveObjectfunction must be implemented in TypeScript. - You should only use built-in JavaScript features, specifically the
Proxyobject. No external libraries are allowed. - The solution should be efficient and not introduce significant overhead for simple operations.
Notes
- Remember that the
handlerobject innew Proxy(target, handler)is where you define your traps. - The
Reflectobject can be very useful within proxy traps for performing the default behavior of the operation on the target object. For instance,Reflect.get(target, prop, receiver)is a good way to get a property's value. - Think about the
receiverargument in proxy traps. It refers to the proxy itself or an object that inherits from the proxy. This is important for correctly handling getters and setters defined on the prototype chain. - For this challenge, you do not need to implement recursive reactivity for nested objects. Focus on the top-level object's properties.