Skip to content

Instantly share code, notes, and snippets.

@AndreiCalazans
Last active April 14, 2020 20:19
Show Gist options
  • Save AndreiCalazans/ef0e4f2461855d42ec92a1db5913a831 to your computer and use it in GitHub Desktop.
Save AndreiCalazans/ef0e4f2461855d42ec92a1db5913a831 to your computer and use it in GitHub Desktop.
'use strict';
var keyList = Object.keys;
function equal(a, b) {
if (a === b) return true;
if (!(a instanceof Object) || !(b instanceof Object)) return false;
var keys = keyList(a);
var length = keys.length;
for (var i = 0; i < length; i++)
if (!(keys[i] in b)) return false;
for (var i = 0; i < length; i++)
if (a[keys[i]] !== b[keys[i]]) return false;
return length === keyList(b).length;
};
const ResultMap = new Map();
const DepMap = new Map();
/*
Use this to garbage collect every 30 seconds.
function garbageCollect() {
ResultMap.clear();
DepMap.clear();
}
setInterval(garbageCollect, 30000);
*/
function garbageCollect() {
ResultMap.clear();
DepMap.clear();
}
setInterval(garbageCollect, 30000);
function createSelector(deps, mapResult) {
return function mapStateToPropsSelector(state, props) {
let isDirty = false;
function selectState(depFn) {
const returnValue = depFn(state, props);
const CurrentState = !DepMap.has(depFn) ? "INITIAL" : "UPDATE";
switch (CurrentState) {
case "INITIAL": {
DepMap.set(depFn, returnValue);
isDirty = true;
return returnValue;
}
case "UPDATE": {
if (!DepMap.get(depFn) || !equal(DepMap.get(depFn), returnValue)) {
DepMap.set(depFn, returnValue);
isDirty = true;
}
return returnValue;
}
}
}
let depsToUse = deps;
if (!Array.isArray(deps)) {
depsToUse = [deps];
}
const depsArray = depsToUse.map(selectState);
let result;
if (isDirty || !ResultMap.has(mapResult) || !equal(ResultMap.get(mapResult)[1], depsArray)) {
result = mapResult.apply({}, depsArray); // this computes again
ResultMap.set(mapResult, [result, depsArray]);
}
return result || ResultMap.get(mapResult)[0];
};
}
exports.createSelector = createSelector;
@AndreiCalazans
Copy link
Author

@AndreiCalazans
Copy link
Author

AndreiCalazans commented Mar 19, 2020

Use this for shallow equal

var keyList = Object.keys;

exports.equal = function equal (a, b) {
  if (a === b) return true;
  if (!(a instanceof Object) || !(b instanceof Object)) return false;

  var keys = keyList(a);
  var length = keys.length;

  for (var i = 0; i < length; i++)
    if (!(keys[i] in b)) return false;

  for (var i = 0; i < length; i++)
    if (a[keys[i]] !== b[keys[i]]) return false;

  return length === keyList(b).length;
};

@AndreiCalazans
Copy link
Author

'use strict';

var keyList = Object.keys;

function equal (a, b) {
  if (a === b) return true;
  if (!(a instanceof Object) || !(b instanceof Object)) return false;

  var keys = keyList(a);
  var length = keys.length;

  for (var i = 0; i < length; i++)
    if (!(keys[i] in b)) return false;

  for (var i = 0; i < length; i++)
    if (a[keys[i]] !== b[keys[i]]) return false;

  return length === keyList(b).length;
};
const ResultMap = new Map();

const DepMap = new Map();

/*
Use this to garbage collect every 30 seconds.
function garbageCollect() {
  ResultMap.clear();
  DepMap.clear();
}
setInterval(garbageCollect, 30000);
*/

function garbageCollect() {
  ResultMap.clear();
  DepMap.clear();
}
setInterval(garbageCollect, 30000);

function createSelector(deps, mapResult) {
  return function mapStateToPropsSelector(state, props) {
    let isDirty = false;

    function selectState(depFn) {
      const returnValue = depFn(state, props);
      const CurrentState = !DepMap.has(depFn) ? "INITIAL" : "UPDATE";

      switch (CurrentState) {
        case "INITIAL": {
          DepMap.set(depFn, returnValue);
          isDirty = true;
          return returnValue;
        }
        case "UPDATE": {
          if (!DepMap.get(depFn) || !equal(DepMap.get(depFn), returnValue)) {
            DepMap.set(depFn, returnValue);
            isDirty = true;
          }
          return returnValue;
        }
      }
    }

    let depsToUse = deps;
    if (!Array.isArray(deps)) {
      depsToUse = [deps];
    }

    const depsArray = depsToUse.map(selectState);

    let result;
    if (isDirty || !ResultMap.has(mapResult) || !equal(ResultMap.get(mapResult)[1], depsArray)) {
      result = mapResult.apply({}, depsArray); // this computes again
      ResultMap.set(mapResult, [result, depsArray]);
    }

    return result || ResultMap.get(mapResult)[0];
  };
}

exports.createSelector = createSelector;

@AndreiCalazans
Copy link
Author

The last update is in regards to toString. It is not performant enough, the downside to not stringifying the function signatures is anonymous and arrow functions with the exact same signature will be duplicated. To mitigate this the client should always prefer shared named function definitions to get the most out of our client cache.

@AndreiCalazans
Copy link
Author

Here is another version: We need to test to see which is faster.


'use strict';

var keyList = Object.keys;

function equal(a, b) {
  if (a === b) return true;
  if (!(a instanceof Object) || !(b instanceof Object)) return false;

  var keys = keyList(a);
  var length = keys.length;

  for (var i = 0; i < length; i++)
    if (!(keys[i] in b)) return false;

  for (var i = 0; i < length; i++)
    if (a[keys[i]] !== b[keys[i]]) return false;

  return length === keyList(b).length;
};

/*
Use this to garbage collect every 30 seconds.
function garbageCollect() {
  ResultMap.clear();
  DepMap.clear();
}
setInterval(garbageCollect, 30000);
*/

// function garbageCollect() {
//   ResultMap.clear();
//   DepMap.clear();
// }

// setInterval(garbageCollect, 30000);

let counter = 0;
let ResponseCache = new Map();
function createSelector(deps, mapResult) {
  let uniqueKey = ++counter;
  ResponseCache.set(uniqueKey, [undefined, undefined]);

  // cachedResponse[uniqueKey]
  // This way you can store the depsArray used by A unique key.
  return function mapStateToPropsSelector(state, props) {
    let depsToUse = deps;
    if (!Array.isArray(deps)) {
      depsToUse = [deps];
    }

    let shouldRecompute = false;
    let depsArray = [];
    // let currentIndex = 0;
    for (let dep of depsToUse) {
      const selectedResult = dep(state, props);
      depsArray.push(selectedResult);
    }
    const currentCache = ResponseCache.get(uniqueKey);

    if (!currentCache[0] || !currentCache[1] || !equal(depsArray, currentCache[1])) {
        ResponseCache.set(uniqueKey, [mapResult.apply({}, depsArray), depsArray]);
    }
    return ResponseCache.get(uniqueKey)[0];
  };
}

exports.createSelector = createSelector;


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment