Aurelia Store, immerjs and objects

Still experimenting things with Aurelia Store and immerjs. I have a component which receive an article object so it can display it. This article comes from the store. On the component, there is a button to mark the article as read. When it is clicked, an action is dispatched to mark the article as read (API request + change in the store). When I try to update the article in the store with:

export const markedAsRead = produce((draft: IState, article: IArticle) => {
    // If we access article directly, immerjs has no knowledge of the modification and the state is mutated.
    const index = articles.map((article) => article.id).indexOf(articleToFind.id);
    draft.rss.unreadArticles[index].isRead = true;
});

I get this error: Unhandled rejection Error: Immer drafts cannot have computed properties. If I test with an object that is not passed to a view model, it works fine (for instance by putting the code below in the constructor of the App class):

 const originalArticle = new Article({status: null});
const baseState = {articles: [originalArticle]};
const nextState = produce(baseState, (draft) => {
    const index = draft.articles.map((article) => article.id).indexOf(originalArticle.id);
    const article = draft.articles[index];
    article.status = 'Read';
});
console.log(nextState.articles[0]);
console.log(originalArticle);

From what I was able to track down, it comes from the fact that Aurelia defined getters and setters on objects for each properties. Because of that, immerjs can’t clone the objects and thus can’t work. From what I read here, it actually makes sense from the immerjs perspective:

The problem with getters is that they can’t be cloned. What should be cloned? The value? But in that case they become ‘static’. Or should the descriptor be copied? But in that case the closure of the getter might be wrong, that is, referring to something it was already referring to, there is no way to tell…

Any thoughts on this? I guess I can always revert to the spread operator but it feels like defeating the point of immerjs.

1 Like

I am not sure if it is the right way, but I used an older version of immer (1.8.2) which worked with this setup. In the end I gave up on using Immer and just used JSON.parse(JSON.stringify()) or Object.assign().

The Aurelia State examples did not work for me with the newest version of Immer.

R

1 Like

It sounds like a nice addition to ease state management. And we have an example for it so I find it a bit disappointing that it is not working.

Maybe we have a way to “un-aurelia” the object to remove these getters and setters and make immer work. @zewa666 do you have an opinion on this?

1 Like

Frankly I haven’t worked with Immer.js anymore aside from the time I’ve created the sample. So you say the sample as it is now with the latest update breaks? I’ll have a look what we can do instead.

1 Like

The example may work because you are only using an int. The problem seems to arise only when you pass an object to a view model like: my-value.bind="myObject"because Aurelia will add setters and getters to myObject.

2 Likes

I gave it another look but this is above what I can figure out on my own. I managed to make two sandboxes to illustrate the issue. If I bind an int like in the example, everything works: https://codesandbox.io/s/aurelia-sandbox-t8nch If I bind an object (here a simple/standard object), I get my error: https://codesandbox.io/s/aurelia-sandbox-gh3so (I actually have to go to https://gh3so.codesandbox.io/) to view the error in the console.

1 Like

one cheap quickfix i used was @connectTo(store => store.state.pipe(pluck('myObject'), map(cloneDeep)))

1 Like

This could work yes.

Where does cloneDeep come from?

1 Like