-
Notifications
You must be signed in to change notification settings - Fork 24
Adding memoization #20
base: master
Are you sure you want to change the base?
Conversation
Awesome @yachaka, thanks a lot! Please give some some time to review the changes. |
@yachaka Thanks again for your PR. Yes, I see the need of a refactoring to avoid some code dupes... If you are have some time for a code review, we can work on this together, otherwise I can try updating the branch myself in the next days. |
@yachaka Thanks for this. Really looking forward to this change. 🎉 |
Sorry, I've broken the PR fixing up some code in master :( The valuable code is still visible and reusable from the diff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some ideas for refactoring 😄
@@ -1,3 +1,3 @@ | |||
{ | |||
"presets": ["es2015"] | |||
"presets": ["es2015", "stage-0"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need stage-0 💣 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure ! I don't remember why I added it, I used ...spread
at some point I think.
@@ -6,6 +6,7 @@ | |||
"scripts": { | |||
"prebuild": "rimraf dist lib", | |||
"build": "babel src --out-dir lib", | |||
"mocha": "mocha --compilers js:babel-core/register", | |||
"test": "mocha --compilers js:babel-core/register --recursive", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks! indeed, no need to --recursive
here!
@@ -1,12 +1,14 @@ | |||
import IterableSchema from 'normalizr/lib/IterableSchema'; | |||
import EntitySchema from 'normalizr/lib/EntitySchema'; | |||
import UnionSchema from 'normalizr/lib/UnionSchema'; | |||
import assign from 'lodash/assign'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does babel.js transpile Object.assign()
? 🤔
Because I'd better use it than adding another dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you need a polyfill ! But that's probably better to include one than using a library function.
@@ -21,13 +23,15 @@ function resolveEntityOrId(entityOrId, entities, schema) { | |||
|
|||
if (isObject(entityOrId)) { | |||
id = getIn(entity, [schema.getIdAttribute()]) | |||
entity = getIn(entities, [key, id]) | |||
} else { | |||
entity = getIn(entities, [key, id]) | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright! Then I'd better write
if (isObject(entityOrId)) {
id = getIn(entity, [schema.getIdAttribute()]);
}
entity = getIn(entities, [key, id]);
const itemSchema = schema.getItemSchema(); | ||
function denormalizeUnion(entity, entities, unionSchema, bag) { | ||
if (!entity.schema) | ||
throw new Error('Expect `entity` to have a schema key as a result from normalizing an union.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change related to the memoization? 🐺
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, I think I just wanted to simplify the function.
@@ -50,14 +73,40 @@ function denormalizeIterable(items, entities, schema, bag) { | |||
* @param {object} bag | |||
* @returns {object|Immutable.Map} | |||
*/ | |||
function denormalizeUnion(entity, entities, schema, bag) { | |||
const itemSchema = schema.getItemSchema(); | |||
function denormalizeUnion(entity, entities, unionSchema, bag) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add an options
parameter here:
denormalizeUnion(entity, entities, unionSchema, options bag)
and pass it some lines below to denormalize(...
if (cache[key][id].entity !== entity) { | ||
cache[key][id].entity = entity | ||
cache[key][id].denormalized = entity | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caching should be abstracted in another file (e.g. cache.js
) to simplify this part and to not expose the caching mechanism here. Something like cache.get()
, cache.set()
, cache.clean()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure ! Feel free to export this part of the code.
let referenceObject = cache[key][id].denormalized | ||
let relationsToUpdate = {} | ||
|
||
/* For each relation in EntitySchema */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what do you mean with relation
. Is that an entity?
Anyway, here I'm lost 😄 I don't fully understand the reasoning behind the code below...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relation
is a relation in the entity Schema
, which are Entities, yes 👍 . I fill this object with all the relations that have been updated - in the loop below - because we want to return the original object if nothing changed !
/** | ||
* Same as denormalize right above, but memoized | ||
*/ | ||
function denormalizeMemoized(obj, entities, schema, bag = {}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we add an options argument here, we could skip the various denormalize*Memoized()
functions and merge their code into the existing denormalize*()
:
export function denormalize(obj, entities, schema, options, bag = {}) {
if (obj === null || typeof obj === 'undefined' || !isObject(schema)) {
return obj;
}
if (schema instanceof EntitySchema) {
return denormalizeEntity(obj, entities, schema, options, bag);
} else if (schema instanceof IterableSchema) {
return denormalizeIterable(obj, entities, schema, options, bag);
} else if (schema instanceof UnionSchema) {
return denormalizeUnion(obj, entities, schema, options, bag);
}
// Ensure we don't mutate it non-immutable objects
const entity = isImmutable(obj) ? obj : merge({}, obj);
return denormalizeObject(entity, entities, schema, options, bag);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure !
return denormalize( | ||
Object.assign({}, entity, { [entity.schema]: entity.id }), | ||
trueEntity, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need the trueEntity? I think a code comment here would be helpful.
I will be able to look into refactoring along your comments this friday. ;) |
Hey guys. Any updates on this? |
@diegohaz I've been a lot busy lately (just started a new job), but I will into refactoring this week (promess 😸). For now, you can use my fork (yachaka/denormalizr) ; it works with memoization using |
@gpbl Updated README, added some code comment. I didn't refactor the denormalize entry point function ; I think it is more readable this way - splitted into 2 functions. In term of performance, it also eliminates a check. Please review 👍 |
@gpbl Sup ? |
Hello,
This is the PR discussed in issue #10.
API follows what has been discussed :
{ memoized: true }
to activate memoization.I've been rather busy so I just added few tests and the overall code could be refactored in some points somehow but it does the job.
Needs the docs to be partly rewritten though to explain how to use/the advantages of using memoization.
Note: the cache is for now global. In the future. It would be necessary to take is as argument to allow the user to maintain a cache for each set of entity he might be using.