Next.js: Circular structure error with rootStore and mobx

Created on 26 Oct 2018  路  10Comments  路  Source: vercel/next.js

Examples bug report

Example name

with-mobx

Describe the bug

TypeError: Converting circular structure to JSON
    at JSON.stringify (<anonymous>)

When changing the store.js into a rootStore as described here I get a circular structure error as above. It seems to be related to the injection of store into the Todo item in the example (which would cause a circular structure...).

I think that the server side store is stringified before sent to frontend, and I guess that is what is causing this. My question is, is it possible to change that behaviour? Like alter the stringify logic?

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Run the with-mobx example
  2. Change the store into a rootstore as here
  3. Watch the backend logs for the error

Expected behavior

I expect the circular structure to be taken care of in some way, it seems to be a pattern that needs to work for mobx to be fully usable. I think I could live with the store not getting shared to front end at all if that would be possible.

System information

  • OS: macOS
  • Browser Chrome
  • Version of Next.js: 7.0.1
good first issue example

Most helpful comment

@alfredvaa The problem is not related to next.js nor the with-mobx example. When you have circular structure in your store you have to handle it somehow by yourself. Next just does JSON.stringify on the props returned by getInitialProps and I don't think it should handle any additional cases.

If you want to use the Mobx rootStore pattern you need to dehydrate (serialize) your store on getInitialProps and rehydrate (deserialize) it in render/constructor. For this, mobx team created a tool called serializr or you could use any other serialization library that handles circular structure.

All 10 comments

I guess that I can use the toJSON method in my stores to limit the circular structure, is that the way one should deal with this?

Are you maybe trying to return a Store Class as a prop from getInitialProps or similar? You should only return objects, which then will be turned automatically to "valid" objects when injected into the html response body by next.js.

When you only return the store instances itself they will be readable and usable for store-rehydration on the client-side.

I don't think that the with-mobx example even contains a getInitialProps method, so no, or am I wrong? :) I can try to set up a repo with the issue and post it here.

The mobx example was recently updated: https://github.com/zeit/next.js/pull/5537

@alfredvaa The problem is not related to next.js nor the with-mobx example. When you have circular structure in your store you have to handle it somehow by yourself. Next just does JSON.stringify on the props returned by getInitialProps and I don't think it should handle any additional cases.

If you want to use the Mobx rootStore pattern you need to dehydrate (serialize) your store on getInitialProps and rehydrate (deserialize) it in render/constructor. For this, mobx team created a tool called serializr or you could use any other serialization library that handles circular structure.

Great reply @sarneeh 馃憤

I think I finally found a decent solution! I presume you ran into this error because you are trying to access other stores via a parent rootStore (the way they tell you to in the mobx docs), but Nextjs breaks when you do so.

My solution is to use export let in each of my stores so that they are accessible to each other.

...
class AuthStore {
  // whatever
}
...

// Allows the store to be initialized isomorphically
export let authStore: AuthState | null = null;

export function initializeAuthStore(initialState: Partial<AuthState>) {
  // Always make a new store if server, otherwise state is shared between requests
  if (!process.browser) {
    return new AuthStore(initialState);
  }
  if (authStore === null) {
    authStore = new AuthStore(initialState);
  }
  return authStore;
}

You can then import { authStore } fromstore/authStorein any of your stores! Just be sureauthStore` is defined before making calls to it.

More on export let
https://stackoverflow.com/questions/32558514/javascript-es6-export-const-vs-export-let/32558929#32558929

@aakagi can you show some sample code with multiple stores using the above approach , not clear now export let solves this problem!

@sarneeh ,do you have an example of how to use custom serializer in getinitialprops?

Was this page helpful?
0 / 5 - 0 ratings