Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocking non viable json objects from main process before sending it to renderer process #32

Open
ernstluring opened this issue Nov 8, 2016 · 4 comments

Comments

@ernstluring
Copy link

ernstluring commented Nov 8, 2016

Hi again,

This looks a bit silly but after I thought I solved this issue it appeared again at the exact same moments. When I store a socket and a electron process in the store (these values can't and don't need to be send to the renderer process) I get the following exception:

Uncaught Exception:
TypeError: Converting circular structure to JSON at store.dispatch
(...\node_modules\redux-electron-store\lib\electronBr....:45)

You mentioned before that this can happen because of the let browserStore = remote.getGlobal(globalName). I would like to hear your thoughts about this and if there is a possible solution for this. At the moment I have the temporarily solution of a second store for these things that don't use the redux enhancer.

Thanks!

@samiskin
Copy link
Owner

samiskin commented Nov 9, 2016

Drat, could you provide me with a code snippet of setting up a reducer and filter to show off this issue?

@ernstluring
Copy link
Author

This is the store and the reducer on the renderer process side:
Setting up store:

export function configureStore(preloadedState?) {
    // Data this renderer process wants to be notified of
    let filter = {
        project: true,
        application: {
            loadingText:true,
            loadingBlocking:true,
            errorText:true,
            gameState:true,
            builderState:true,
            builderErrors:true
        },
        releaseCache: true
    }

    let enhancer = compose(
        applyMiddleware(thunkMiddleware),
        electronEnhancer({filter})
    ) as GenericStoreEnhancer

    const store = createStore<IStudioStore>(
        reducers,
        preloadedState,
        enhancer
    )
    return store
}
export interface IStudioApplication {
    // The currently showing loading text
    loadingText?:string;
    loadingBlocking?:boolean;

    errorText?:string;
    // The currently selected tab
    tab:ApplicationTab;

    // The current state of the game
     gameState:GameState;

    // The current state of the builder
     builderState:BuilderState;

    // Builder errors
    builderErrors:{[file: string]: string[]}

    // The current selected tool tab
    toolTab:ToolTab
}

The reducer:

export default handleActions<IStudioApplication, any>({
    [SELECT_TAB]: (state, action) => {
        return Object.assign({}, state, {tab: action.payload})
    },
    [SELECT_TOOL_TAB]: (state, action) => <IStudioApplication>Object.assign({}, state, { toolTab: action.payload }),
}, initialState)

And setting up store on main process

export interface IStore {
    /** Holds project information */
    project: IProject,

    /** Holds application information */
    application: IApplication,

    /** Holds the release cache information */
    releaseCache: IReleaseCache
}
export interface IApplication {
    // The currently showing loading text
    loadingText?:string;
    loadingBlocking?:boolean;

    errorText?:string;

    // The current state of the game
    gameState:GameState;
    gameProcess:ChildProcess;
    gameSocket:net.Socket;

    // The current state of the builder
    builderState:BuilderState;
    builderProcess:ChildProcess;
    builderSocket:net.Socket;

    // Builder errors
    builderErrors:{[file: string]: string[]}
}
export function configureStore(preloadedState?:IStore) {
    return new Promise<Redux.Store<IStore>>((resolve, reject) => {
        let enhancer = compose(
            applyMiddleware(thunkMiddleware),
            electronEnhancer()
        ) as GenericStoreEnhancer

        const store = createStore<IStore>(
            reducers,
            preloadedState,
            enhancer
        )
        resolve(store)
    })
}

@samiskin
Copy link
Owner

I wasn't able to precisely reproduce your error, but I did have other errors happening right at startup, as in order to do anything I run a recursive objectDifference function on the state to find out what changed, and that breaks on recursive structures. I'm not sure how performant it would be to add more logic to check if it is recursive, as this stuff happens on every dispatch.

I guess what I would have to do is merge the objectDifference and fillShape steps such that the objectDifference logic only runs on data which passes any of the filters, and then explicitly require that no recursive data structures pass through any filter (which is already an undocumented requirement anyway).

After #33, I think the global[globalName] = () => JSON.stringify(store.getState()) will also cause a problem here. A possible solution is to have the renderer get that state using a synchronous ipc call when it registers, and have main reply with a filtered version of the state for the renderers initial state. The possible tradeoff here is that anything not passing the filter will be undefined in the renderer.

I doubt I'll be able to get to this for a while due to university, sorry 😞. Feel free to fork the repo and try out solutions yourself if its a pressing matter, and hopefully what I said in the previous paragraphs helps with that.

@daviestar
Copy link

Hello, I have run into a similar error but in a different scenario. Error is with this stringify:

if (shouldForwardUpdate) {
  ipcRenderer.send(
    `${globalName}-renderer-dispatch`,
    JSON.stringify({ action, clientId })
  );
}

I have a redux store with keys for movies, series, seasons and episodes, each keyed by id. In mapStateToProps if I try to piece the series, seasons and episodes back together I get the error (I realise this isn't best practice, I was just starting to flesh out the functionality):

const mapStateToProps = ({incoming: {queue: {movies, shows, seasons, episodes}}}) => ({
  movies: Object.keys(movies).map(id => movies[id]),
  shows: Object.keys(shows).map(id => shows[id])
    .map(show => {
      show.seasons = show.seasonIds
        .map(id => seasons[id])
        .map(season => {
          season.episodes = season.episodeIds
            .map(id => episodes[id])
          return season
        })
      return show
    })
})
TypeError: Converting circular structure to JSON

there's no error like this:

const mapStateToProps = ({incoming: {queue: {movies, shows, seasons, episodes}}}) => ({
  movies: Object.keys(movies).map(id => movies[id]),
  shows: Object.keys(shows).map(id => shows[id])
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants