Skip to content

Commit 1cb3871

Browse files
committed
Cleanup enableUseRefAccessWarning flag
I don't think this flag has a path forward in the current implementation. The detection by stack trace is too brittle to detect the lazy initialization pattern reliably (see e.g. some internal tests that expect the warning because they use lazy intialization, but a slightly different pattern then the expected pattern. I think a new version of this could be to fully ban ref access during render with an alternative API for the exceptional cases that today require ref access during render.
1 parent 93f9179 commit 1cb3871

13 files changed

+15
-246
lines changed

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 3 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import {
3333
enableDebugTracing,
3434
enableSchedulingProfiler,
3535
enableCache,
36-
enableUseRefAccessWarning,
3736
enableLazyContextPropagation,
3837
enableTransitionTracing,
3938
enableUseMemoCacheHook,
@@ -2348,71 +2347,9 @@ function getCallerStackFrame(): string {
23482347

23492348
function mountRef<T>(initialValue: T): {current: T} {
23502349
const hook = mountWorkInProgressHook();
2351-
if (enableUseRefAccessWarning) {
2352-
if (__DEV__) {
2353-
// Support lazy initialization pattern shown in docs.
2354-
// We need to store the caller stack frame so that we don't warn on subsequent renders.
2355-
let hasBeenInitialized = initialValue != null;
2356-
let lazyInitGetterStack = null;
2357-
let didCheckForLazyInit = false;
2358-
2359-
// Only warn once per component+hook.
2360-
let didWarnAboutRead = false;
2361-
let didWarnAboutWrite = false;
2362-
2363-
let current = initialValue;
2364-
const ref = {
2365-
get current() {
2366-
if (!hasBeenInitialized) {
2367-
didCheckForLazyInit = true;
2368-
lazyInitGetterStack = getCallerStackFrame();
2369-
} else if (currentlyRenderingFiber !== null && !didWarnAboutRead) {
2370-
if (
2371-
lazyInitGetterStack === null ||
2372-
lazyInitGetterStack !== getCallerStackFrame()
2373-
) {
2374-
didWarnAboutRead = true;
2375-
console.warn(
2376-
'%s: Unsafe read of a mutable value during render.\n\n' +
2377-
'Reading from a ref during render is only safe if:\n' +
2378-
'1. The ref value has not been updated, or\n' +
2379-
'2. The ref holds a lazily-initialized value that is only set once.\n',
2380-
getComponentNameFromFiber(currentlyRenderingFiber) || 'Unknown',
2381-
);
2382-
}
2383-
}
2384-
return current;
2385-
},
2386-
set current(value: any) {
2387-
if (currentlyRenderingFiber !== null && !didWarnAboutWrite) {
2388-
if (hasBeenInitialized || !didCheckForLazyInit) {
2389-
didWarnAboutWrite = true;
2390-
console.warn(
2391-
'%s: Unsafe write of a mutable value during render.\n\n' +
2392-
'Writing to a ref during render is only safe if the ref holds ' +
2393-
'a lazily-initialized value that is only set once.\n',
2394-
getComponentNameFromFiber(currentlyRenderingFiber) || 'Unknown',
2395-
);
2396-
}
2397-
}
2398-
2399-
hasBeenInitialized = true;
2400-
current = value;
2401-
},
2402-
};
2403-
Object.seal(ref);
2404-
hook.memoizedState = ref;
2405-
return ref;
2406-
} else {
2407-
const ref = {current: initialValue};
2408-
hook.memoizedState = ref;
2409-
return ref;
2410-
}
2411-
} else {
2412-
const ref = {current: initialValue};
2413-
hook.memoizedState = ref;
2414-
return ref;
2415-
}
2350+
const ref = {current: initialValue};
2351+
hook.memoizedState = ref;
2352+
return ref;
24162353
}
24172354

24182355
function updateRef<T>(initialValue: T): {current: T} {

packages/react-reconciler/src/__tests__/useRef-test.internal.js

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -160,24 +160,6 @@ describe('useRef', () => {
160160
});
161161
});
162162

163-
// @gate enableUseRefAccessWarning
164-
it('should warn about reads during render', async () => {
165-
function Example() {
166-
const ref = useRef(123);
167-
let value;
168-
expect(() => {
169-
value = ref.current;
170-
}).toWarnDev([
171-
'Example: Unsafe read of a mutable value during render.',
172-
]);
173-
return value;
174-
}
175-
176-
await act(() => {
177-
ReactNoop.render(<Example />);
178-
});
179-
});
180-
181163
it('should not warn about lazy init during render', async () => {
182164
function Example() {
183165
const ref1 = useRef(null);
@@ -221,113 +203,6 @@ describe('useRef', () => {
221203
});
222204
});
223205

224-
// @gate enableUseRefAccessWarning
225-
it('should warn about unconditional lazy init during render', async () => {
226-
function Example() {
227-
const ref1 = useRef(null);
228-
const ref2 = useRef(undefined);
229-
230-
if (shouldExpectWarning) {
231-
expect(() => {
232-
ref1.current = 123;
233-
}).toWarnDev([
234-
'Example: Unsafe write of a mutable value during render',
235-
]);
236-
expect(() => {
237-
ref2.current = 123;
238-
}).toWarnDev([
239-
'Example: Unsafe write of a mutable value during render',
240-
]);
241-
} else {
242-
ref1.current = 123;
243-
ref1.current = 123;
244-
}
245-
246-
// But only warn once
247-
ref1.current = 345;
248-
ref1.current = 345;
249-
250-
return null;
251-
}
252-
253-
let shouldExpectWarning = true;
254-
await act(() => {
255-
ReactNoop.render(<Example />);
256-
});
257-
258-
// Should not warn again on update.
259-
shouldExpectWarning = false;
260-
await act(() => {
261-
ReactNoop.render(<Example />);
262-
});
263-
});
264-
265-
// @gate enableUseRefAccessWarning
266-
it('should warn about reads to ref after lazy init pattern', async () => {
267-
function Example() {
268-
const ref1 = useRef(null);
269-
const ref2 = useRef(undefined);
270-
271-
// Read 1: safe because lazy init:
272-
if (ref1.current === null) {
273-
ref1.current = 123;
274-
}
275-
if (ref2.current === undefined) {
276-
ref2.current = 123;
277-
}
278-
279-
let value;
280-
expect(() => {
281-
value = ref1.current;
282-
}).toWarnDev(['Example: Unsafe read of a mutable value during render']);
283-
expect(() => {
284-
value = ref2.current;
285-
}).toWarnDev(['Example: Unsafe read of a mutable value during render']);
286-
287-
// But it should only warn once.
288-
value = ref1.current;
289-
value = ref2.current;
290-
291-
return value;
292-
}
293-
294-
await act(() => {
295-
ReactNoop.render(<Example />);
296-
});
297-
});
298-
299-
// @gate enableUseRefAccessWarning
300-
it('should warn about writes to ref after lazy init pattern', async () => {
301-
function Example() {
302-
const ref1 = useRef(null);
303-
const ref2 = useRef(undefined);
304-
// Read: safe because lazy init:
305-
if (ref1.current === null) {
306-
ref1.current = 123;
307-
}
308-
if (ref2.current === undefined) {
309-
ref2.current = 123;
310-
}
311-
312-
expect(() => {
313-
ref1.current = 456;
314-
}).toWarnDev([
315-
'Example: Unsafe write of a mutable value during render',
316-
]);
317-
expect(() => {
318-
ref2.current = 456;
319-
}).toWarnDev([
320-
'Example: Unsafe write of a mutable value during render',
321-
]);
322-
323-
return null;
324-
}
325-
326-
await act(() => {
327-
ReactNoop.render(<Example />);
328-
});
329-
});
330-
331206
it('should not warn about reads or writes within effect', async () => {
332207
function Example() {
333208
const ref = useRef(123);

packages/shared/ReactFeatureFlags.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,6 @@ export const enableRenderableContext = __NEXT_MAJOR__;
193193
// when we plan to enable them.
194194
// -----------------------------------------------------------------------------
195195

196-
export const enableUseRefAccessWarning = false;
197-
198196
// Enables time slicing for updates that aren't wrapped in startTransition.
199197
export const forceConcurrentByDefaultForTesting = false;
200198

packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,5 @@ export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
2626
export const enableInfiniteRenderLoopDetection = __VARIANT__;
2727
export const enableRenderableContext = __VARIANT__;
2828
export const enableUnifiedSyncLane = __VARIANT__;
29-
export const enableUseRefAccessWarning = __VARIANT__;
3029
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
3130
export const useModernStrictMode = __VARIANT__;

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const {
2828
enableInfiniteRenderLoopDetection,
2929
enableRenderableContext,
3030
enableUnifiedSyncLane,
31-
enableUseRefAccessWarning,
3231
passChildrenWhenCloningPersistedNodes,
3332
useModernStrictMode,
3433
} = dynamicFlags;

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ export const enableRetryLaneExpiration = false;
9393
export const retryLaneExpirationMs = 5000;
9494
export const syncLaneExpirationMs = 250;
9595
export const transitionLaneExpirationMs = 5000;
96-
export const enableUseRefAccessWarning = false;
9796
export const disableSchedulerTimeoutInWorkLoop = false;
9897
export const enableLazyContextPropagation = false;
9998
export const enableLegacyHidden = false;

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ export const retryLaneExpirationMs = 5000;
4949
export const syncLaneExpirationMs = 250;
5050
export const transitionLaneExpirationMs = 5000;
5151

52-
export const enableUseRefAccessWarning = false;
53-
5452
export const disableSchedulerTimeoutInWorkLoop = false;
5553
export const enableLazyContextPropagation = false;
5654
export const enableLegacyHidden = false;

packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ export const enableCPUSuspense = true;
4444
export const enableUseMemoCacheHook = true;
4545
export const enableUseEffectEventHook = false;
4646
export const favorSafetyOverHydrationPerf = true;
47-
export const enableUseRefAccessWarning = false;
4847
export const enableInfiniteRenderLoopDetection = false;
4948
export const enableRenderableContext = false;
5049

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ export const retryLaneExpirationMs = 5000;
5151
export const syncLaneExpirationMs = 250;
5252
export const transitionLaneExpirationMs = 5000;
5353

54-
export const enableUseRefAccessWarning = false;
55-
5654
export const disableSchedulerTimeoutInWorkLoop = false;
5755
export const enableLazyContextPropagation = false;
5856
export const enableLegacyHidden = false;

packages/shared/forks/ReactFeatureFlags.www-dynamic.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
export const disableInputAttributeSyncing = __VARIANT__;
1717
export const disableIEWorkarounds = __VARIANT__;
1818
export const enableBigIntSupport = __VARIANT__;
19-
export const enableLegacyFBSupport = __VARIANT__;
20-
export const enableUseRefAccessWarning = __VARIANT__;
2119
export const disableSchedulerTimeoutInWorkLoop = __VARIANT__;
2220
export const enableLazyContextPropagation = __VARIANT__;
2321
export const forceConcurrentByDefaultForTesting = __VARIANT__;

0 commit comments

Comments
 (0)