Skip to content

Commit

Permalink
Merge pull request #3772 from Methuselah96/replace-reducer-rebase
Browse files Browse the repository at this point in the history
resolves #3767
resolves #3482
  • Loading branch information
markerikson committed Feb 12, 2023
2 parents 5c6d100 + 8edc1ff commit a5e4f95
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 118 deletions.
33 changes: 10 additions & 23 deletions src/createStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
StoreEnhancer,
Dispatch,
Observer,
ExtendState,
ListenerCallback
} from './types/store'
import { Action } from './types/actions'
Expand Down Expand Up @@ -43,7 +42,7 @@ import { kindOf } from './utils/kindOf'
export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
/**
* @deprecated
*
Expand Down Expand Up @@ -73,12 +72,12 @@ export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
): Store<S, A, StateExt> & Ext {
if (typeof reducer !== 'function') {
throw new Error(
`Expected the root reducer to be a function. Instead, received: '${kindOf(
Expand Down Expand Up @@ -115,7 +114,7 @@ export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
) as Store<S, A, StateExt> & Ext
}

let currentReducer = reducer
Expand Down Expand Up @@ -291,11 +290,8 @@ export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
* implement a hot reloading mechanism for Redux.
*
* @param nextReducer The reducer for the store to use instead.
* @returns The same store instance with a new reducer in place.
*/
function replaceReducer<NewState, NewActions extends A>(
nextReducer: Reducer<NewState, NewActions>
): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
function replaceReducer(nextReducer: Reducer<S, A>): void {
if (typeof nextReducer !== 'function') {
throw new Error(
`Expected the nextReducer to be a function. Instead, received: '${kindOf(
Expand All @@ -304,22 +300,13 @@ export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
)
}

// TODO: do this more elegantly
;(currentReducer as unknown as Reducer<NewState, NewActions>) = nextReducer
currentReducer = nextReducer

// This action has a similar effect to ActionTypes.INIT.
// Any reducers that existed in both the new and old rootReducer
// will receive the previous state. This effectively populates
// the new state tree with any relevant data from the old one.
dispatch({ type: ActionTypes.REPLACE } as A)
// change the type of the store by casting it to the new store
return store as unknown as Store<
ExtendState<NewState, StateExt>,
NewActions,
StateExt,
Ext
> &
Ext
}

/**
Expand Down Expand Up @@ -377,7 +364,7 @@ export function createStore<S, A extends Action, Ext = {}, StateExt = never>(
getState,
replaceReducer,
[$$observable]: observable
} as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
} as unknown as Store<S, A, StateExt> & Ext
return store
}

Expand Down Expand Up @@ -419,7 +406,7 @@ export function legacy_createStore<
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
/**
* Creates a Redux store that holds the state tree.
*
Expand Down Expand Up @@ -459,7 +446,7 @@ export function legacy_createStore<
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
export function legacy_createStore<
S,
A extends Action,
Expand All @@ -469,6 +456,6 @@ export function legacy_createStore<
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
): Store<S, A, StateExt> & Ext {
return createStore(reducer, preloadedState as any, enhancer)
}
18 changes: 7 additions & 11 deletions src/types/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,11 @@ export type Observer<T> = {
* @template S The type of state held by this store.
* @template A the type of actions which may be dispatched by this store.
* @template StateExt any extension to state from store enhancers
* @template Ext any extensions to the store from store enhancers
*/
export interface Store<
S = any,
A extends Action = AnyAction,
StateExt = never,
Ext = {}
StateExt = never
> {
/**
* Dispatches an action. It is the only way to trigger a state change.
Expand Down Expand Up @@ -174,7 +172,7 @@ export interface Store<
*
* @returns The current state tree of your application.
*/
getState(): S
getState(): ExtendState<S, StateExt>

/**
* Adds a change listener. It will be called any time an action is
Expand Down Expand Up @@ -211,17 +209,15 @@ export interface Store<
*
* @param nextReducer The reducer for the store to use instead.
*/
replaceReducer<NewState, NewActions extends Action>(
nextReducer: Reducer<NewState, NewActions>
): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext
replaceReducer(nextReducer: Reducer<S, A>): void

/**
* Interoperability point for observable/reactive libraries.
* @returns {observable} A minimal observable of state changes.
* For more information, see the observable proposal:
* https://github.com/tc39/proposal-observable
*/
[Symbol.observable](): Observable<S>
[Symbol.observable](): Observable<ExtendState<S, StateExt>>
}

/**
Expand All @@ -239,12 +235,12 @@ export interface StoreCreator {
<S, A extends Action, Ext = {}, StateExt = never>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
<S, A extends Action, Ext = {}, StateExt = never>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
): Store<S, A, StateExt> & Ext
}

/**
Expand Down Expand Up @@ -277,4 +273,4 @@ export type StoreEnhancerStoreCreator<Ext = {}, StateExt = never> = <
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>
) => Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
) => Store<S, A, StateExt> & Ext
16 changes: 11 additions & 5 deletions test/combineReducers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,31 +327,35 @@ describe('Utils', () => {
const ACTION = { type: 'ACTION' }

it('should return an updated state when additional reducers are passed to combineReducers', function () {
const originalCompositeReducer = combineReducers({ foo })
type State = { foo: {}; bar?: {} }

const originalCompositeReducer = combineReducers<State>({ foo })
const store = createStore(originalCompositeReducer)

store.dispatch(ACTION)

const initialState = store.getState()

store.replaceReducer(combineReducers({ foo, bar }))
store.replaceReducer(combineReducers<State>({ foo, bar }))
store.dispatch(ACTION)

const nextState = store.getState()
expect(nextState).not.toBe(initialState)
})

it('should return an updated state when reducers passed to combineReducers are changed', function () {
type State = { foo?: {}; bar: {}; baz?: {} }

const baz = (state = {}) => state

const originalCompositeReducer = combineReducers({ foo, bar })
const originalCompositeReducer = combineReducers<State>({ foo, bar })
const store = createStore(originalCompositeReducer)

store.dispatch(ACTION)

const initialState = store.getState()

store.replaceReducer(combineReducers({ baz, bar }))
store.replaceReducer(combineReducers<State>({ baz, bar }))
store.dispatch(ACTION)

const nextState = store.getState()
Expand All @@ -374,7 +378,9 @@ describe('Utils', () => {
})

it('should return an updated state when one of more reducers passed to the combineReducers are removed', function () {
const originalCompositeReducer = combineReducers({ foo, bar })
const originalCompositeReducer = combineReducers<{ foo?: {}; bar: {} }>(
{ foo, bar }
)
const store = createStore(originalCompositeReducer)

store.dispatch(ACTION)
Expand Down
2 changes: 1 addition & 1 deletion test/createStore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ describe('createStore', () => {
console.error = jest.fn()

const store = createStore(
combineReducers({
combineReducers<{ x?: number; y: { z: number; w?: number } }>({
x: (s = 0, _) => s,
y: combineReducers({
z: (s = 0, _) => s,
Expand Down
18 changes: 0 additions & 18 deletions test/replaceReducers.spec.ts

This file was deleted.

Loading

0 comments on commit a5e4f95

Please sign in to comment.