Hone logo
Hone
Problems

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:

  1. Intercepting property reads: When a property is accessed on the reactive object, you should log a message indicating the property name being read.
  2. 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.
  3. 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.
  4. 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 createReactiveObject function should accept a single argument: target (a plain JavaScript object).
  • It should return a Proxy object that wraps the target.
  • The Proxy should implement the get, set, and deleteProperty traps.
  • get trap: Log "Reading property: [propertyName]".
  • set trap: Log "Setting property: [propertyName] to [newValue]". After logging, the value should be updated on the target object.
  • deleteProperty trap: Log "Deleting property: [propertyName]". After logging, the property should be deleted from the target object.

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 undefined or 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 createReactiveObject function must be implemented in TypeScript.
  • You should only use built-in JavaScript features, specifically the Proxy object. No external libraries are allowed.
  • The solution should be efficient and not introduce significant overhead for simple operations.

Notes

  • Remember that the handler object in new Proxy(target, handler) is where you define your traps.
  • The Reflect object 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 receiver argument 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.
Loading editor...
typescript