diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js
index 97de8867b2b..12a3defdf25 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js
@@ -209,6 +209,23 @@ describe('ReactDOMServerHooks', () => {
expect(domNode.textContent).toEqual('0');
});
+ itRenders('with default reducer state value', async render => {
+ function reducer(state = 0, action) {
+ return action === 'increment' ? state + 1 : state;
+ }
+ function Counter() {
+ let [count] = useReducer(reducer);
+ yieldValue('Render: ' + count);
+ return ;
+ }
+
+ const domNode = await render();
+
+ expect(clearYields()).toEqual(['Render: 0', 0]);
+ expect(domNode.tagName).toEqual('SPAN');
+ expect(domNode.textContent).toEqual('0');
+ });
+
itRenders('lazy initialization with initialAction', async render => {
function reducer(state, action) {
return action === 'increment' ? state + 1 : state;
diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js
index 025b51a8c02..acae8d4b845 100644
--- a/packages/react-dom/src/server/ReactPartialRendererHooks.js
+++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js
@@ -225,7 +225,7 @@ export function useState(
}
export function useReducer(
- reducer: (S, A) => S,
+ reducer: (S, A | any) => S,
initialState: S,
initialAction: A | void | null,
): [S, Dispatch] {
@@ -277,7 +277,12 @@ export function useReducer(
initialState = reducer(initialState, initialAction);
}
currentlyRenderingComponent = component;
- workInProgressHook.memoizedState = initialState;
+ if (initialState === undefined) {
+ // use default reducer state value
+ workInProgressHook.memoizedState = reducer(initialState, {});
+ } else {
+ workInProgressHook.memoizedState = initialState;
+ }
const queue: UpdateQueue = (workInProgressHook.queue = {
last: null,
dispatch: null,
diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js
index 4af03961690..f4a6400d3fc 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.js
@@ -411,7 +411,7 @@ export function useState(
}
export function useReducer(
- reducer: (S, A) => S,
+ reducer: (S, A | any) => S,
initialState: S,
initialAction: A | void | null,
): [S, Dispatch] {
@@ -557,7 +557,15 @@ export function useReducer(
initialState = reducer(initialState, initialAction);
}
currentlyRenderingFiber = fiber;
- workInProgressHook.memoizedState = workInProgressHook.baseState = initialState;
+ if (initialState === undefined) {
+ // use default reducer state value
+ workInProgressHook.memoizedState = workInProgressHook.baseState = reducer(
+ initialState,
+ {},
+ );
+ } else {
+ workInProgressHook.memoizedState = workInProgressHook.baseState = initialState;
+ }
queue = workInProgressHook.queue = {
last: null,
dispatch: null,
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
index 74d69fac78d..3ebdb105df1 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js
@@ -525,6 +525,44 @@ describe('ReactHooksWithNoopRenderer', () => {
expect(ReactNoop.getChildren()).toEqual([span('Count: -2')]);
});
+ it('accepts default reducer state value as initial state', () => {
+ const INCREMENT = 'INCREMENT';
+ const DECREMENT = 'DECREMENT';
+
+ function reducer(state = 0, action) {
+ switch (action) {
+ case 'INCREMENT':
+ return state + 1;
+ case 'DECREMENT':
+ return state - 1;
+ default:
+ return state;
+ }
+ }
+
+ function Counter(props, ref) {
+ const [count, dispatch] = useReducer(reducer);
+ useImperativeHandle(ref, () => ({dispatch}));
+ return ;
+ }
+
+ Counter = forwardRef(Counter);
+ const counter = React.createRef(null);
+ ReactNoop.render();
+ ReactNoop.flush();
+ expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
+
+ counter.current.dispatch(INCREMENT);
+ ReactNoop.flush();
+ expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
+
+ counter.current.dispatch(DECREMENT);
+ counter.current.dispatch(DECREMENT);
+ counter.current.dispatch(DECREMENT);
+ ReactNoop.flush();
+ expect(ReactNoop.getChildren()).toEqual([span('Count: -2')]);
+ });
+
it('accepts an initial action', () => {
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';