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

RFC: warn when returning different hooks on subsequent renders #14585

Merged
merged 41 commits into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4d8a8cf
warn when returning different hooks on next render
Jan 14, 2019
0daee73
lint
Jan 14, 2019
623fb58
review changes
Jan 14, 2019
1204a0c
cleaner detection location
Jan 14, 2019
d597fcd
redundant comments
Jan 14, 2019
1c7314f
different EffectHook / LayoutEffectHook
Jan 14, 2019
5221e91
prettier
Jan 14, 2019
bcbce28
top level currentHookType
Jan 14, 2019
a604ea1
nulling currentHookType
Jan 14, 2019
8b22c87
small enhancements
Jan 17, 2019
95aa003
hook order checks for useContext/useImperative
Jan 17, 2019
cbf4068
Merge branch 'master' into wrong-hook-order
Jan 18, 2019
4676b6b
prettier
Jan 18, 2019
5a94b54
stray whitespace
Jan 18, 2019
aca87cd
move some bits around
Jan 18, 2019
428a3eb
better errors
Jan 18, 2019
6cd966c
pass tests
Jan 18, 2019
13debbb
lint, flow
Jan 18, 2019
65ebacb
Merge remote-tracking branch 'upstream/master' into wrong-hook-order
Jan 18, 2019
9bd7051
show a before - after diff
Jan 18, 2019
0eea976
Merge remote-tracking branch 'upstream/master' into wrong-hook-order
Jan 18, 2019
f09966d
an error stack in the warning
Jan 18, 2019
e366069
lose currentHookMatches, fix a test
Jan 19, 2019
d8c4236
tidy
Jan 19, 2019
4801b12
clear the mismatch only in dev
Jan 19, 2019
c77e448
pass flow
Jan 19, 2019
b6fb3ca
side by side diff
Jan 21, 2019
055ebf6
tweak warning
Jan 21, 2019
c7a872b
Merge remote-tracking branch 'upstream/master' into wrong-hook-order
Jan 21, 2019
e06b72b
pass flow
Jan 21, 2019
190e9b1
dedupe warnings per fiber, nits
Jan 21, 2019
f37f4fd
better format
Jan 21, 2019
26fd179
Merge remote-tracking branch 'upstream/master' into wrong-hook-order
Jan 21, 2019
3e8a217
nit
Jan 21, 2019
c2d686a
Merge remote-tracking branch 'upstream/master' into wrong-hook-order
Jan 21, 2019
f493675
fix bad merge, pass flow
Jan 22, 2019
a985e72
lint
Jan 22, 2019
1621a2d
missing hooktype enum
Jan 22, 2019
5ecda4c
merge currentHookType/currentHookNameInDev, fix nits
Jan 22, 2019
d4860ca
lint
Jan 22, 2019
3ad6523
final nits
Jan 22, 2019
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
46 changes: 33 additions & 13 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
} from './ReactFiberScheduler';

import invariant from 'shared/invariant';
import warning from 'shared/warning';
import areHookInputsEqual from 'shared/areHookInputsEqual';

type Update<A> = {
Expand All @@ -47,7 +48,18 @@ type UpdateQueue<A> = {
dispatch: any,
};

type HookType =
| 'state'
| 'reducer'
| 'context'
| 'ref'
| 'effect'
| 'callback'
| 'memo'
| 'imperative-handle';
threepointone marked this conversation as resolved.
Show resolved Hide resolved

export type Hook = {
hookType: HookType,
threepointone marked this conversation as resolved.
Show resolved Hide resolved
memoizedState: any,

baseState: any,
Expand Down Expand Up @@ -237,8 +249,9 @@ export function resetHooks(): void {
numberOfReRenders = 0;
}

function createHook(): Hook {
function createHook(hookType: HookType): Hook {
return {
hookType,
threepointone marked this conversation as resolved.
Show resolved Hide resolved
memoizedState: null,

baseState: null,
Expand All @@ -249,8 +262,13 @@ function createHook(): Hook {
};
}

function cloneHook(hook: Hook): Hook {
function cloneHook(hook: Hook, hookType: HookType): Hook {
threepointone marked this conversation as resolved.
Show resolved Hide resolved
if (__DEV__ && hookType !== hook.hookType) {
warning(false, 'Bad hook order');
}

return {
hookType: hook.hookType,
memoizedState: hook.memoizedState,

baseState: hook.baseState,
Expand All @@ -261,18 +279,18 @@ function cloneHook(hook: Hook): Hook {
};
}

function createWorkInProgressHook(): Hook {
function createWorkInProgressHook(hookType: HookType): Hook {
if (workInProgressHook === null) {
// This is the first hook in the list
if (firstWorkInProgressHook === null) {
isReRender = false;
currentHook = firstCurrentHook;
if (currentHook === null) {
// This is a newly mounted hook
workInProgressHook = createHook();
workInProgressHook = createHook(hookType);
} else {
// Clone the current hook.
workInProgressHook = cloneHook(currentHook);
workInProgressHook = cloneHook(currentHook, hookType);
}
firstWorkInProgressHook = workInProgressHook;
} else {
Expand All @@ -287,15 +305,15 @@ function createWorkInProgressHook(): Hook {
let hook;
if (currentHook === null) {
// This is a newly mounted hook
hook = createHook();
hook = createHook(hookType);
} else {
currentHook = currentHook.next;
if (currentHook === null) {
// This is a newly mounted hook
hook = createHook();
hook = createHook(hookType);
} else {
// Clone the current hook.
hook = cloneHook(currentHook);
hook = cloneHook(currentHook, hookType);
}
}
// Append to the end of the list
Expand Down Expand Up @@ -346,7 +364,9 @@ export function useReducer<S, A>(
initialAction: A | void | null,
): [S, Dispatch<A>] {
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
workInProgressHook = createWorkInProgressHook();
workInProgressHook = createWorkInProgressHook(
reducer === basicStateReducer ? 'state' : 'reducer',
);
let queue: UpdateQueue<A> | null = (workInProgressHook.queue: any);
if (queue !== null) {
// Already have a queue, so this is an update.
Expand Down Expand Up @@ -499,7 +519,7 @@ function pushEffect(tag, create, destroy, inputs) {

export function useRef<T>(initialValue: T): {current: T} {
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
workInProgressHook = createWorkInProgressHook();
workInProgressHook = createWorkInProgressHook('ref');
let ref;

if (workInProgressHook.memoizedState === null) {
Expand Down Expand Up @@ -535,7 +555,7 @@ export function useEffect(

function useEffectImpl(fiberEffectTag, hookEffectTag, create, inputs): void {
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
workInProgressHook = createWorkInProgressHook();
workInProgressHook = createWorkInProgressHook('effect');

let nextInputs = inputs !== undefined && inputs !== null ? inputs : [create];
let destroy = null;
Expand Down Expand Up @@ -593,7 +613,7 @@ export function useCallback<T>(
inputs: Array<mixed> | void | null,
): T {
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
workInProgressHook = createWorkInProgressHook();
workInProgressHook = createWorkInProgressHook('callback');

const nextInputs =
inputs !== undefined && inputs !== null ? inputs : [callback];
Expand All @@ -614,7 +634,7 @@ export function useMemo<T>(
inputs: Array<mixed> | void | null,
): T {
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
workInProgressHook = createWorkInProgressHook();
workInProgressHook = createWorkInProgressHook('memo');

const nextInputs =
inputs !== undefined && inputs !== null ? inputs : [nextCreate];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,29 @@ describe('ReactHooks', () => {
const root = ReactTestRenderer.create(<App />);
expect(root.toJSON()).toMatchSnapshot();
});

it('warns on using a different hooks on subsequent renders', () => {
threepointone marked this conversation as resolved.
Show resolved Hide resolved
const {useState, useReducer} = React;
function App(props) {
if (props.flip) {
const [count, setCount] = useState(0);
const [state, dispatch] = useReducer(
(state, action) => action.payload,
0,
);
return null;
} else {
const [state, dispatch] = useReducer(
(state, action) => action.payload,
0,
);
const [count, setCount] = useState(0);
return null;
}
}
let root = ReactTestRenderer.create(<App flip={false} />);
expect(() => {
root.update(<App flip={true} />);
}).toWarnDev(['Warning: Bad hook order\n' + ' in App (at **)']);
});
});