mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
Clear effect tags from a fiber that suspends in non-concurrent mode
Even though we commit the fiber in an incomplete state, we shouldn't fire any lifecycles or effects. We already did this for classes, but now with useEffect, the same is needed for other types of work, too.
This commit is contained in:
+6
-5
@@ -248,15 +248,16 @@ function throwException(
|
||||
);
|
||||
sourceFiber.effectTag &= ~Incomplete;
|
||||
|
||||
// We're going to commit this fiber even though it didn't complete.
|
||||
// But we shouldn't call any lifecycle methods or callbacks. Remove
|
||||
// all lifecycle effect tags.
|
||||
sourceFiber.effectTag &= ~LifecycleEffectMask;
|
||||
|
||||
if (sourceFiber.tag === ClassComponent) {
|
||||
// We're going to commit this fiber even though it didn't complete.
|
||||
// But we shouldn't call any lifecycle methods or callbacks. Remove
|
||||
// all lifecycle effect tags.
|
||||
sourceFiber.effectTag &= ~LifecycleEffectMask;
|
||||
const current = sourceFiber.alternate;
|
||||
if (current === null) {
|
||||
// This is a new mount. Change the tag so it's not mistaken for a
|
||||
// completed component. For example, we should not call
|
||||
// completed class component. For example, we should not call
|
||||
// componentWillUnmount if it is deleted.
|
||||
sourceFiber.tag = IncompleteClassComponent;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ describe('ReactSuspense', () => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
|
||||
ReactFeatureFlags.enableHooks = true;
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
// JestReact = require('jest-react');
|
||||
@@ -526,6 +527,51 @@ describe('ReactSuspense', () => {
|
||||
expect(root).toMatchRenderedOutput('B');
|
||||
});
|
||||
|
||||
it('suspends in a component that also contains useEffect', () => {
|
||||
const {useLayoutEffect} = React;
|
||||
|
||||
function AsyncTextWithEffect(props) {
|
||||
const text = props.text;
|
||||
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
ReactTestRenderer.unstable_yield('Did commit: ' + text);
|
||||
},
|
||||
[text],
|
||||
);
|
||||
|
||||
try {
|
||||
TextResource.read([props.text, props.ms]);
|
||||
ReactTestRenderer.unstable_yield(text);
|
||||
return text;
|
||||
} catch (promise) {
|
||||
if (typeof promise.then === 'function') {
|
||||
ReactTestRenderer.unstable_yield(`Suspend! [${text}]`);
|
||||
} else {
|
||||
ReactTestRenderer.unstable_yield(`Error! [${text}]`);
|
||||
}
|
||||
throw promise;
|
||||
}
|
||||
}
|
||||
|
||||
function App({text}) {
|
||||
return (
|
||||
<Suspense fallback={<Text text="Loading..." />}>
|
||||
<AsyncTextWithEffect text={text} ms={100} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
ReactTestRenderer.create(<App text="A" />);
|
||||
expect(ReactTestRenderer).toHaveYielded(['Suspend! [A]', 'Loading...']);
|
||||
jest.advanceTimersByTime(500);
|
||||
expect(ReactTestRenderer).toHaveYielded([
|
||||
'Promise resolved [A]',
|
||||
'A',
|
||||
'Did commit: A',
|
||||
]);
|
||||
});
|
||||
|
||||
it('retries when an update is scheduled on a timed out tree', () => {
|
||||
let instance;
|
||||
class Stateful extends React.Component {
|
||||
|
||||
Reference in New Issue
Block a user