Skip to content

Commit

Permalink
Regression test: startTransition in render phase
Browse files Browse the repository at this point in the history
useTransition uses the state hook as part of its implementation, so we
need to fork it in the re-render dispatcher, too.
  • Loading branch information
acdlite committed Dec 16, 2019
1 parent a37b65e commit 9c445d4
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
17 changes: 14 additions & 3 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,17 @@ function updateTransition(
return [start, isPending];
}

function rerenderTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
const [isPending, setPending] = rerenderState(false);
const start = updateCallback(startTransition.bind(null, setPending, config), [
setPending,
config,
]);
return [start, isPending];
}

function dispatchAction<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
Expand Down Expand Up @@ -1421,7 +1432,7 @@ const HooksDispatcherOnRerender: Dispatcher = {
useDebugValue: updateDebugValue,
useResponder: createResponderListener,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useTransition: rerenderTransition,
};

let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
Expand Down Expand Up @@ -1920,7 +1931,7 @@ if (__DEV__) {
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
updateHookTypesDev();
return updateTransition(config);
return rerenderTransition(config);
},
};

Expand Down Expand Up @@ -2313,7 +2324,7 @@ if (__DEV__) {
currentHookNameInDev = 'useTransition';
warnInvalidHookAccess();
updateHookTypesDev();
return updateTransition(config);
return rerenderTransition(config);
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,38 @@ describe('ReactHooksWithNoopRenderer', () => {
expect(Scheduler).toFlushAndYield(['B:0']);
expect(root).toMatchRenderedOutput(<span prop="B:0" />);
});

// TODO: This should probably warn
it('calling startTransition inside render phase', async () => {
function Foo({signal}) {
return (
<Suspense fallback="Loading...">
<Bar signal={signal} />
</Suspense>
);
}

let startTransition;
function Bar() {
let [counter, setCounter] = useState(0);
let [_startTransition] = useTransition();
startTransition = _startTransition;

// Increment a counter every time the signal changes
if (counter === 0) {
startTransition(() => {
setCounter(c => c + 1);
});
}

return <Text text={counter} />;
}

const root = ReactNoop.createRoot();
root.render(<Foo signal={true} />);
expect(Scheduler).toFlushAndYield([1]);
expect(root).toMatchRenderedOutput(<span prop={1} />);
});
});

describe('useReducer', () => {
Expand Down

0 comments on commit 9c445d4

Please sign in to comment.