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

fix: align enhancer style with standards to wrap the middleware #284

Merged
merged 2 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/mainStateSyncEnhancer.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { ipcMain, webContents } from 'electron'
import { Action, applyMiddleware, Middleware, StoreCreator, StoreEnhancer } from 'redux'
import {
Action,
compose,
Dispatch,
Middleware,
MiddlewareAPI,
StoreCreator,
StoreEnhancer,
} from 'redux'
import { IPCEvents } from './constants'
import {
defaultMainOptions,
MainStateSyncEnhancerOptions,
} from './options/MainStateSyncEnhancerOptions'

import { preventDoubleInitialization, stopForwarding, validateAction } from './utils'

function createMiddleware(options: MainStateSyncEnhancerOptions) {
Expand Down Expand Up @@ -60,7 +67,21 @@ export const mainStateSyncEnhancer = (options = defaultMainOptions): StoreEnhanc
) => {
preventDoubleInitialization()
const middleware = createMiddleware(options)
return (reducer, state) => {
return createStore(reducer, state, applyMiddleware(middleware))
return (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState)

let dispatch = store.dispatch

const middlewareAPI: MiddlewareAPI<Dispatch<any>> = {
getState: store.getState,
dispatch,
}

dispatch = compose<Dispatch>(middleware(middlewareAPI))(dispatch)

return {
...store,
dispatch,
}
}
}
41 changes: 28 additions & 13 deletions src/rendererStateSyncEnhancer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { ipcRenderer } from 'electron'
import { Action, applyMiddleware, Middleware, StoreCreator, StoreEnhancer } from 'redux'
import {
Action,
compose,
Dispatch,
Middleware,
MiddlewareAPI,
StoreCreator,
StoreEnhancer,
} from 'redux'
import { IPCEvents } from './constants'
import { fetchInitialState, fetchInitialStateAsync } from './fetchState'
import { replaceState, withStoreReplacer } from './fetchState/replaceState'
import { defaultRendererOptions, RendererStateSyncEnhancerOptions } from './options/RendererStateSyncEnhancerOptions'

import {
defaultRendererOptions,
RendererStateSyncEnhancerOptions,
} from './options/RendererStateSyncEnhancerOptions'
import { preventDoubleInitialization, stopForwarding, validateAction } from './utils'

const createMiddleware = (options: RendererStateSyncEnhancerOptions): Middleware => (store) => {
Expand Down Expand Up @@ -35,11 +45,12 @@ export const rendererStateSyncEnhancer = (options = defaultRendererOptions): Sto
preventDoubleInitialization()

return (reducer, state) => {
const middleware = createMiddleware(options)

const initialState = options.lazyInit ? state : fetchInitialState<typeof state>(options)
const store = createStore(
options.lazyInit ? withStoreReplacer(reducer) : reducer,
initialState,
applyMiddleware(createMiddleware(options))
initialState
)

if (options.lazyInit) {
Expand All @@ -48,14 +59,18 @@ export const rendererStateSyncEnhancer = (options = defaultRendererOptions): Sto
})
}

// TODO: this needs some ❤️
// XXX: TypeScript is dumb. If you return the call to createStore
// immediately it's fine, but even assigning it to a constant and returning
// will make it freak out. We fix this with the line below the return.
return store
let dispatch = store.dispatch

const middlewareAPI: MiddlewareAPI<Dispatch<any>> = {
getState: store.getState,
dispatch,
}

dispatch = compose<Dispatch>(middleware(middlewareAPI))(dispatch)

// TODO: this needs some ❤️
// XXX: Even though this is unreachable, it fixes the type signature????
return (store as unknown) as any
return {
...store,
dispatch,
}
}
}
10 changes: 9 additions & 1 deletion tests/counter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const init: CounterState = {

const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
const SET_COUNT_MIDDLEWARE = 'SET_COUNT_MIDDLEWARE'

type IncrementAction = {
type: typeof INCREMENT
Expand All @@ -17,14 +18,21 @@ type DecrementAction = {
type: typeof DECREMENT
}

export type Actions = IncrementAction | DecrementAction
type SetCountMiddlewareAction = {
type: typeof SET_COUNT_MIDDLEWARE
payload: number
}

export type Actions = IncrementAction | DecrementAction | SetCountMiddlewareAction

export const reducer = (state = init, action: Actions): CounterState => {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 }
case DECREMENT:
return { ...state, count: state.count - 1 }
case SET_COUNT_MIDDLEWARE:
return { ...state, count: action.payload }
default:
return state
}
Expand Down
33 changes: 33 additions & 0 deletions tests/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,37 @@ describe('End to End Tests', () => {
// eslint-disable-next-line @typescript-eslint/await-thenable
expect(await app.browserWindow.getTitle()).toEqual('9')
})

it('should be able to increment value from main process', async () => {
expect(await getText('#value')).toEqual('10')
expect(await app.browserWindow.getTitle()).toEqual('10')

await click('#mainIncrement')

expect(await getText('#value')).toEqual('11')
// eslint-disable-next-line @typescript-eslint/await-thenable
expect(await app.browserWindow.getTitle()).toEqual('11')
})

it('should be able to use middleware when dispatching from renderer process', async () => {
expect(await getText('#value')).toEqual('10')
expect(await app.browserWindow.getTitle()).toEqual('10')

await click('#setCountMiddleware')

expect(await getText('#value')).toEqual('99')
// eslint-disable-next-line @typescript-eslint/await-thenable
expect(await app.browserWindow.getTitle()).toEqual('99')
})

it('should be able to use middleware when dispatching from main process', async () => {
expect(await getText('#value')).toEqual('10')
expect(await app.browserWindow.getTitle()).toEqual('10')

await click('#mainsetCountMiddleware')

expect(await getText('#value')).toEqual('99')
// eslint-disable-next-line @typescript-eslint/await-thenable
expect(await app.browserWindow.getTitle()).toEqual('99')
})
})
17 changes: 14 additions & 3 deletions tests/e2e/main/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import path from 'path'
import url from 'url'
import { app, BrowserWindow } from 'electron'
import { createStore } from 'redux'
import { app, BrowserWindow, ipcMain } from 'electron'
import { applyMiddleware, compose, createStore } from 'redux'
import { reducer } from '../../counter'
import { mainStateSyncEnhancer } from '../../..'
import { countMiddleware } from '../../middleware'

const isDevelopment = process.env.NODE_ENV !== 'production'

const defaultState = {
count: 10,
}

const store = createStore(reducer, defaultState, mainStateSyncEnhancer())
const middleware = applyMiddleware(countMiddleware)
const enhancer = compose(middleware, mainStateSyncEnhancer())
const store = createStore(reducer, defaultState, enhancer)

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
Expand Down Expand Up @@ -44,6 +47,14 @@ function createWindow() {
}
renderValue()

ipcMain.once('mainsetCountMiddleware', () => {
store.dispatch({ type: 'SET_COUNT_MIDDLEWARE', payload: 9 })
})

ipcMain.once('mainIncrement', () => {
store.dispatch({ type: 'INCREMENT' })
})

// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
Expand Down
23 changes: 21 additions & 2 deletions tests/e2e/renderer/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { createStore } from 'redux'
import { applyMiddleware, compose, createStore } from 'redux'
import { reducer } from '../../counter'
import { rendererStateSyncEnhancer } from '../../../'
import { countMiddleware } from '../../middleware'
import { ipcRenderer } from 'electron'

const store = createStore(reducer, rendererStateSyncEnhancer())
const middleware = applyMiddleware(countMiddleware)
const enhancer = compose(middleware, rendererStateSyncEnhancer())

const store = createStore(reducer, enhancer)

function mount() {
document.getElementById('app')!.innerHTML = `
<p>
Clicked: <span id="value">0</span> times </br>
<button id="increment">+</button>
<button id="decrement">-</button>
<button id="setCountMiddleware">#</button>
<button id="mainsetCountMiddleware">#</button>
<button id="mainIncrement">#</button>
</p>
`

Expand All @@ -20,6 +29,16 @@ function mount() {
document.getElementById('decrement')!.addEventListener('click', () => {
store.dispatch({ type: 'DECREMENT' })
})

document.getElementById('setCountMiddleware')!.addEventListener('click', () => {
store.dispatch({ type: 'SET_COUNT_MIDDLEWARE', payload: 9 })
})
document.getElementById('mainsetCountMiddleware')!.addEventListener('click', () => {
ipcRenderer.send('mainsetCountMiddleware')
})
document.getElementById('mainIncrement')!.addEventListener('click', () => {
ipcRenderer.send('mainIncrement')
})
}

function renderValue() {
Expand Down
8 changes: 8 additions & 0 deletions tests/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Middleware } from 'redux'

export const countMiddleware: Middleware = () => (next) => (action) => {
return next({
...action,
payload: 99,
})
}