redux-electron-store
redux-electron-store copied to clipboard
Blocking non viable json objects from main process before sending it to renderer process
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!
Drat, could you provide me with a code snippet of setting up a reducer and filter to show off this issue?
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)
})
}
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 renderer
s 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.
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])
})