0.2.0 • Published 5 years ago

@reversecurrent/track-changes v0.2.0

Weekly downloads
1
License
ISC
Repository
-
Last release
5 years ago

track-changes

Track an object or array for changes

Target object can have nested objects/arrays which will also be tracked Uses the Proxy API and Reflect API.

Install

$ npm install track-changes

Usage

// Import the 
import trackChanges from 'track-changes';
// Define the target object to track for changes.
// Target object can have nested objects/arrays which will also be tracked
let target = {
    name: 'furqan',
    age: 30,
    isSmart: false,
    address: {
        geo: {
            lat: 23.56,
            lon: 56.78,
            encoding: {
                type: 'map'
            }
        },
        city: 'bangalore',
        zipcode: '56004'
    },
    companies: {
        professional: [
            {
                name: 'Amazon',
                title: 'Software Engineer'
            },
            {
                name: 'Microsoft',
                title: 'Technical Lead'
            }
        ],
        freelancing: [
            {
                name: 'Reverse Current',
                title: 'Consultant'
            }
        ]
    },
    hobbies: ['tennis']
};

// Define the tracker function which will be invoked when the target object
// is accessed/modified
const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType); // Set | Get | Delete | Sort | Assign
                console.log(propertyKey); // Property name being accessed/modified/deleted
                console.log(newValue); // New value of the property. null for Delete
                console.log(oldValue); // Old value of the property. null for Get , Delete
            };
// Call the trackChanges function passing the target object and tracker function
// It will return a proxy object using which  we access/modify the target
let watchedObject = trackChanges(target, trackerFn);

// Set a property value. This will:
//   1. Set the value in target property
//   2. Invoke tracker function passing the parameters
watchedObject.name = 'Furqan - Modified';
// opType = 'Set' , propertyKey = 'name', newValue = 'Furqan -modified', oldValue = 'Furqan'

// Set a nested property value
watchedObject.address.geo.encoding.type = 'geo';
// opType = 'Set' , propertyKey = 'type', newValue = 'geo', oldValue = 'map'

API

trackChanges(target, trackerFn)

Returns a version of target that is tracked for changes. It's the exact same object, with some Proxy traps.

target

Type: Object

Object to watch for changes.

trackerFn

Type: Function Function that is invoked when target changes/accessed

opType

Type: String Operation being performed on the target. One of these values: 'Get', 'Set', 'Delete', 'Assign', 'Sort'

propertyKey

Type: String Property name being changed/accessed

newValue

Type: any New value of the property

oldValue

Type: any Old value of the property

Scenarios

Track when target properties are accessed

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Get
                console.log(propertyKey) // 'name' | 'type'
                console.log(newValue) // 'furqan' | 'map'
            };
            const proxy = trackChanges(target, trackerFn);
            // Accessing a property will invoke the tracker function
            proxy.name;
            // Accessing a nested property will also invoke the tracker function
            proxy.address.geo.encoding.type;

Track when target properties (including nested) are modified

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Set
                console.log(propertyKey) // 'name'('type' when type nested property is set)
                console.log(newValue) // 'Furqan - modified' ('geo' when type nested property is set)
            };
            const proxy = trackChanges(target, trackerFn);
            // Setting a property will invoke the tracker function
            proxy.name = 'Furqan - modified';
            // Setting a nested property will also invoke the tracker function
            proxy.address.geo.encoding.type = 'geo';

Track when nested array properties are modified

const trackerFn = (opType, propertyKey, newValue, oldValue) => {
                console.log(opType);  // OpType.Set
                console.log(propertyKey) // 'professional'
                console.log(newValue) // [
                                            // {name: 'Amazon',title: 'Software Engineer'},
                                            // {name: 'Microsoft',title: 'Technical Lead'},
                console.log(oldValue) // [
                                            // {name: 'Amazon',title: 'Software Engineer'},
                                            // {name: 'Microsoft',title: 'Technical Lead'},
                                            //{ 'name': 'Google', 'title': 'Design Lead' } ]
            };
const proxy = trackChanges(target, trackerFn);
// Setting a nested array property will invoke the tracker function
proxy.companies.professional.push({ 'name': 'Google', 'title': 'Design Lead' });

Track when array value is get/set using index

let hobbies = ['tennis'];
            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Get);
                expect(propertyKey).toEqual('0'); 
                expect(value).toEqual('tennis');
            };
            const proxy = trackChanges(hobbies, trackerFn);
            // Accessing an array item using index will invoke the tracker function
            proxy[0];


            // Setting an array item will invoke the tracker function
            const trackerFn2 = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(propertyKey).toEqual('1'); 
                expect(value).toEqual('reading');
            };
            const proxy2 = trackChanges(hobbies, trackerFn2);
            proxy2[1] = 'reading';

Track when setting array value using push

let hobbies = ['tennis'];
            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Get);
                expect(propertyKey).toEqual('push');
                expect(value).toEqual(['tennis', 'reading']);
            };
            const proxy = trackChanges(hobbies, trackerFn);
            // Setting an array item using push will invoke the tracker function
            proxy.push('reading');

Track deleting property on target

            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Delete);
                expect(propertyKey).toEqual('type');
            };
            const proxy = trackChanges(target, trackerFn);
            delete proxy.address.geo.encoding.type;

Track when calling Object.assign on targets

            const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(propertyKey).toEqual('phone');
                expect(newValue).toEqual('1234');
            };
            const proxy = trackChanges(target, trackerFn);
            Object.assign(proxy, { 'phone': '1234' })

Track when setting a nested object on an array target

let array = [1, 2, { a: 3 }];
const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Set);
                expect(newValue).toEqual(4);
};
const proxy = trackChanges(array, trackerFn);
proxy[2].a = 4;

Track when calling sort on array target

let array = [4,2,1,6,7];
const trackerFn = (opType, propertyKey, value, oldValue) => {
                expect(opType).toEqual(OpType.Sort);
                expect(newValue).toEqual([1,2,4,6,7]);
};
const proxy = trackChanges(array, trackerFn);
proxy.sort();

Allow only access to known object properties

const obj = {foo: true};
const proxy = trackChanges(obj, trackerFn);
// Throws a TypeError when you try to access an unknown property
console.log(proxy.bar);
//=> [TypeError] Unknown property: bar