mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Use global state for hasForceUpdate instead of persisting to queue (#12808)
* Use global state for `hasForceUpdate` instead of persisting to queue Fixes a bug where `hasForceUpdate` was not reset on commit. Ideally we'd use a tuple and return `hasForceUpdate` from `processUpdateQueue`. * Remove underscore and add comment * Remove temporary variables
This commit is contained in:
+28
-32
@@ -32,6 +32,8 @@ import {StrictMode} from './ReactTypeOfMode';
|
||||
import {
|
||||
enqueueUpdate,
|
||||
processUpdateQueue,
|
||||
checkHasForceUpdateAfterProcessing,
|
||||
resetHasForceUpdateBeforeProcessing,
|
||||
createUpdate,
|
||||
ReplaceState,
|
||||
ForceUpdate,
|
||||
@@ -235,14 +237,6 @@ export default function(
|
||||
newState,
|
||||
newContext,
|
||||
) {
|
||||
if (
|
||||
workInProgress.updateQueue !== null &&
|
||||
workInProgress.updateQueue.hasForceUpdate
|
||||
) {
|
||||
// If forceUpdate was called, disregard sCU.
|
||||
return true;
|
||||
}
|
||||
|
||||
const instance = workInProgress.stateNode;
|
||||
const ctor = workInProgress.type;
|
||||
if (typeof instance.shouldComponentUpdate === 'function') {
|
||||
@@ -789,6 +783,8 @@ export default function(
|
||||
}
|
||||
}
|
||||
|
||||
resetHasForceUpdateBeforeProcessing();
|
||||
|
||||
const oldState = workInProgress.memoizedState;
|
||||
let newState = (instance.state = oldState);
|
||||
let updateQueue = workInProgress.updateQueue;
|
||||
@@ -806,10 +802,7 @@ export default function(
|
||||
oldProps === newProps &&
|
||||
oldState === newState &&
|
||||
!hasContextChanged() &&
|
||||
!(
|
||||
workInProgress.updateQueue !== null &&
|
||||
workInProgress.updateQueue.hasForceUpdate
|
||||
)
|
||||
!checkHasForceUpdateAfterProcessing()
|
||||
) {
|
||||
// If an update was already in progress, we should schedule an Update
|
||||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||||
@@ -828,14 +821,16 @@ export default function(
|
||||
newState = workInProgress.memoizedState;
|
||||
}
|
||||
|
||||
const shouldUpdate = checkShouldComponentUpdate(
|
||||
workInProgress,
|
||||
oldProps,
|
||||
newProps,
|
||||
oldState,
|
||||
newState,
|
||||
newContext,
|
||||
);
|
||||
const shouldUpdate =
|
||||
checkHasForceUpdateAfterProcessing() ||
|
||||
checkShouldComponentUpdate(
|
||||
workInProgress,
|
||||
oldProps,
|
||||
newProps,
|
||||
oldState,
|
||||
newState,
|
||||
newContext,
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
// In order to support react-lifecycles-compat polyfilled components,
|
||||
@@ -922,6 +917,8 @@ export default function(
|
||||
}
|
||||
}
|
||||
|
||||
resetHasForceUpdateBeforeProcessing();
|
||||
|
||||
const oldState = workInProgress.memoizedState;
|
||||
let newState = (instance.state = oldState);
|
||||
let updateQueue = workInProgress.updateQueue;
|
||||
@@ -940,10 +937,7 @@ export default function(
|
||||
oldProps === newProps &&
|
||||
oldState === newState &&
|
||||
!hasContextChanged() &&
|
||||
!(
|
||||
workInProgress.updateQueue !== null &&
|
||||
workInProgress.updateQueue.hasForceUpdate
|
||||
)
|
||||
!checkHasForceUpdateAfterProcessing()
|
||||
) {
|
||||
// If an update was already in progress, we should schedule an Update
|
||||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||||
@@ -977,14 +971,16 @@ export default function(
|
||||
}
|
||||
}
|
||||
|
||||
const shouldUpdate = checkShouldComponentUpdate(
|
||||
workInProgress,
|
||||
oldProps,
|
||||
newProps,
|
||||
oldState,
|
||||
newState,
|
||||
newContext,
|
||||
);
|
||||
const shouldUpdate =
|
||||
checkHasForceUpdateAfterProcessing() ||
|
||||
checkShouldComponentUpdate(
|
||||
workInProgress,
|
||||
oldProps,
|
||||
newProps,
|
||||
oldState,
|
||||
newState,
|
||||
newContext,
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
// In order to support react-lifecycles-compat polyfilled components,
|
||||
|
||||
+16
-7
@@ -131,9 +131,6 @@ export type UpdateQueue<State> = {
|
||||
|
||||
firstCapturedEffect: Update<State> | null,
|
||||
lastCapturedEffect: Update<State> | null,
|
||||
|
||||
// TODO: Workaround for lack of tuples. Could use global state instead.
|
||||
hasForceUpdate: boolean,
|
||||
};
|
||||
|
||||
export const UpdateState = 0;
|
||||
@@ -141,6 +138,11 @@ export const ReplaceState = 1;
|
||||
export const ForceUpdate = 2;
|
||||
export const CaptureUpdate = 3;
|
||||
|
||||
// Global state that is reset at the beginning of calling `processUpdateQueue`.
|
||||
// It should only be read right after calling `processUpdateQueue`, via
|
||||
// `checkHasForceUpdateAfterProcessing`.
|
||||
let hasForceUpdate = false;
|
||||
|
||||
let didWarnUpdateInsideUpdate;
|
||||
let currentlyProcessingQueue;
|
||||
export let resetCurrentlyProcessingQueue;
|
||||
@@ -164,7 +166,6 @@ export function createUpdateQueue<State>(baseState: State): UpdateQueue<State> {
|
||||
lastEffect: null,
|
||||
firstCapturedEffect: null,
|
||||
lastCapturedEffect: null,
|
||||
hasForceUpdate: false,
|
||||
};
|
||||
return queue;
|
||||
}
|
||||
@@ -183,8 +184,6 @@ function cloneUpdateQueue<State>(
|
||||
firstCapturedUpdate: null,
|
||||
lastCapturedUpdate: null,
|
||||
|
||||
hasForceUpdate: false,
|
||||
|
||||
firstEffect: null,
|
||||
lastEffect: null,
|
||||
|
||||
@@ -423,7 +422,7 @@ function getStateFromUpdate<State>(
|
||||
return Object.assign({}, prevState, partialState);
|
||||
}
|
||||
case ForceUpdate: {
|
||||
queue.hasForceUpdate = true;
|
||||
hasForceUpdate = true;
|
||||
return prevState;
|
||||
}
|
||||
}
|
||||
@@ -437,6 +436,8 @@ export function processUpdateQueue<State>(
|
||||
instance: any,
|
||||
renderExpirationTime: ExpirationTime,
|
||||
): void {
|
||||
hasForceUpdate = false;
|
||||
|
||||
if (
|
||||
queue.expirationTime === NoWork ||
|
||||
queue.expirationTime > renderExpirationTime
|
||||
@@ -595,6 +596,14 @@ function callCallback(callback, context) {
|
||||
callback.call(context);
|
||||
}
|
||||
|
||||
export function resetHasForceUpdateBeforeProcessing() {
|
||||
hasForceUpdate = false;
|
||||
}
|
||||
|
||||
export function checkHasForceUpdateAfterProcessing(): boolean {
|
||||
return hasForceUpdate;
|
||||
}
|
||||
|
||||
export function commitUpdateQueue<State>(
|
||||
finishedWork: Fiber,
|
||||
finishedQueue: UpdateQueue<State>,
|
||||
|
||||
@@ -1115,6 +1115,29 @@ describe('ReactIncremental', () => {
|
||||
expect(ops).toEqual(['Foo', 'Bar', 'Baz', 'Bar', 'Baz']);
|
||||
});
|
||||
|
||||
it('should clear forceUpdate after update is flushed', () => {
|
||||
let a = 0;
|
||||
|
||||
class Foo extends React.PureComponent {
|
||||
render() {
|
||||
const msg = `A: ${a}, B: ${this.props.b}`;
|
||||
ReactNoop.yield(msg);
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
const foo = React.createRef(null);
|
||||
ReactNoop.render(<Foo ref={foo} b={0} />);
|
||||
expect(ReactNoop.flush()).toEqual(['A: 0, B: 0']);
|
||||
|
||||
a = 1;
|
||||
foo.current.forceUpdate();
|
||||
expect(ReactNoop.flush()).toEqual(['A: 1, B: 0']);
|
||||
|
||||
ReactNoop.render(<Foo ref={foo} b={0} />);
|
||||
expect(ReactNoop.flush()).toEqual([]);
|
||||
});
|
||||
|
||||
xit('can call sCU while resuming a partly mounted component', () => {
|
||||
let ops = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user