Skip to content

Commit

Permalink
Warn on readContext() in SSR inside useMemo and useReducer
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Jan 23, 2019
1 parent d1d4dda commit 63bf091
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,36 @@ describe('ReactDOMServerHooks', () => {
expect(domNode.textContent).toEqual('undefined');
});
});

describe('readContext', () => {
function readContext(Context, observedBits) {
const dispatcher =
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.ReactCurrentDispatcher.current;
return dispatcher.readContext(Context, observedBits);
}

itRenders('with a warning inside useMemo and useReducer', async render => {
const Context = React.createContext(42);

function ReadInMemo(props) {
let count = React.useMemo(() => readContext(Context), []);
return <Text text={count} />;
}

function ReadInReducer(props) {
let [count, dispatch] = React.useReducer(() => readContext(Context));
if (count !== 42) {
dispatch();
}
return <Text text={count} />;
}

const domNode1 = await render(<ReadInMemo />, 1);
expect(domNode1.textContent).toEqual('42');

const domNode2 = await render(<ReadInReducer />, 1);
expect(domNode2.textContent).toEqual('42');
});
});
});
29 changes: 29 additions & 0 deletions packages/react-dom/src/server/ReactPartialRendererHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ let renderPhaseUpdates: Map<UpdateQueue<any>, Update<any>> | null = null;
let numberOfReRenders: number = 0;
const RE_RENDER_LIMIT = 25;

let shouldWarnAboutReadingContextInDEV = false;

// In DEV, this is the name of the currently executing primitive hook
let currentHookNameInDev: ?string;

Expand Down Expand Up @@ -137,6 +139,9 @@ function createWorkInProgressHook(): Hook {

export function prepareToUseHooks(componentIdentity: Object): void {
currentlyRenderingComponent = componentIdentity;
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = false;
}

// The following should have already been reset
// didScheduleRenderPhaseUpdate = false;
Expand Down Expand Up @@ -173,6 +178,9 @@ export function finishHooks(
numberOfReRenders = 0;
renderPhaseUpdates = null;
workInProgressHook = null;
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = false;
}

// These were reset above
// currentlyRenderingComponent = null;
Expand All @@ -191,6 +199,15 @@ function readContext<T>(
): T {
let threadID = currentThreadID;
validateContextBounds(context, threadID);
if (__DEV__) {
warning(
!shouldWarnAboutReadingContextInDEV,
'Context can only be read while React is rendering. ' +
'In classes, you can read it in the render method or getDerivedStateFromProps. ' +
'In function components, you can read it directly in the function body, but not ' +
'inside Hooks like useReducer() or useMemo().',
);
}
return context[threadID];
}

Expand Down Expand Up @@ -255,7 +272,13 @@ export function useReducer<S, A>(
const action = update.action;
// Temporarily clear to forbid calling Hooks.
currentlyRenderingComponent = null;
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = true;
}
newState = reducer(newState, action);
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = false;
}
currentlyRenderingComponent = component;
update = update.next;
} while (update !== null);
Expand Down Expand Up @@ -311,8 +334,14 @@ function useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T {

// Temporarily clear to forbid calling Hooks.
currentlyRenderingComponent = null;
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = true;
}
const nextValue = nextCreate();
currentlyRenderingComponent = component;
if (__DEV__) {
shouldWarnAboutReadingContextInDEV = false;
}
workInProgressHook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
Expand Down

0 comments on commit 63bf091

Please sign in to comment.