Hone logo
Hone
Problems

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:

  1. Create a function reactive that takes an object as input and returns a proxy object.
  2. The proxy object should intercept property access (getting) and modification (setting).
  3. When a property is accessed, the callback function provided to reactive should be executed with the target object and the key being accessed.
  4. When a property is modified, the callback function should be executed with the target object, the key being modified, and the new value.
  5. The reactive function should handle nested objects recursively, making the entire object tree reactive.

Key Requirements:

  • Use the Proxy API.
  • Implement both get and set traps.
  • 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 reactive function 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 reactive function 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 get and set traps.
  • The original object should not be mutated directly.

Notes

  • Consider using recursion to handle nested objects.
  • The get trap should return the value of the property.
  • The set trap should return true to 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.
Loading editor...
typescript