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 19, 2019
1 parent 33394cb commit 6f4d988
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 6 deletions.
43 changes: 37 additions & 6 deletions packages/react-reconciler/src/ReactFiberHooks.js
Expand Up @@ -1198,6 +1198,26 @@ function updateDeferredValue<T>(
return prevValue;
}

function rerenderDeferredValue<T>(
value: T,
config: TimeoutConfig | void | null,
): T {
const [prevValue, setValue] = rerenderState(value);
updateEffect(
() => {
const previousConfig = ReactCurrentBatchConfig.suspense;
ReactCurrentBatchConfig.suspense = config === undefined ? null : config;
try {
setValue(value);
} finally {
ReactCurrentBatchConfig.suspense = previousConfig;
}
},
[value, config],
);
return prevValue;
}

function startTransition(setPending, config, callback) {
const priorityLevel = getCurrentPriorityLevel();
runWithPriority(
Expand Down Expand Up @@ -1243,6 +1263,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 @@ -1420,8 +1451,8 @@ const HooksDispatcherOnRerender: Dispatcher = {
useState: rerenderState,
useDebugValue: updateDebugValue,
useResponder: createDeprecatedResponderListener,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useDeferredValue: rerenderDeferredValue,
useTransition: rerenderTransition,
};

let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
Expand Down Expand Up @@ -1913,14 +1944,14 @@ if (__DEV__) {
useDeferredValue<T>(value: T, config: TimeoutConfig | void | null): T {
currentHookNameInDev = 'useDeferredValue';
updateHookTypesDev();
return updateDeferredValue(value, config);
return rerenderDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
updateHookTypesDev();
return updateTransition(config);
return rerenderTransition(config);
},
};

Expand Down Expand Up @@ -2305,15 +2336,15 @@ if (__DEV__) {
currentHookNameInDev = 'useDeferredValue';
warnInvalidHookAccess();
updateHookTypesDev();
return updateDeferredValue(value, config);
return rerenderDeferredValue(value, config);
},
useTransition(
config: SuspenseConfig | void | null,
): [(() => void) => void, boolean] {
currentHookNameInDev = 'useTransition';
warnInvalidHookAccess();
updateHookTypesDev();
return updateTransition(config);
return rerenderTransition(config);
},
};
}
Expand Up @@ -645,6 +645,29 @@ describe('ReactHooksWithNoopRenderer', () => {
expect(Scheduler).toFlushAndYield(['B:0']);
expect(root).toMatchRenderedOutput(<span prop="B:0" />);
});

// TODO: This should probably warn
it.experimental('calling startTransition inside render phase', async () => {
let startTransition;
function App() {
let [counter, setCounter] = useState(0);
let [_startTransition] = useTransition();
startTransition = _startTransition;

if (counter === 0) {
startTransition(() => {
setCounter(c => c + 1);
});
}

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

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

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

0 comments on commit 6f4d988

Please sign in to comment.