diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
index 6cb57a8560b73..397b739546dcc 100644
--- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js
@@ -474,12 +474,12 @@ describe('ReactDOMServerHooks', () => {
describe('useRef', () => {
itRenders('basic render', async render => {
function Counter(props) {
- const count = useRef(0);
- return Count: {count.current};
+ const ref = useRef();
+ return Hi;
}
const domNode = await render();
- expect(domNode.textContent).toEqual('Count: 0');
+ expect(domNode.textContent).toEqual('Hi');
});
itRenders(
@@ -487,18 +487,16 @@ describe('ReactDOMServerHooks', () => {
async render => {
function Counter(props) {
const [count, setCount] = useState(0);
- const ref = useRef(count);
+ const ref = useRef();
if (count < 3) {
const newCount = count + 1;
-
- ref.current = newCount;
setCount(newCount);
}
yieldValue(count);
- return Count: {ref.current};
+ return Count: {count};
}
const domNode = await render();
@@ -513,7 +511,7 @@ describe('ReactDOMServerHooks', () => {
let firstRef = null;
function Counter(props) {
const [count, setCount] = useState(0);
- const ref = useRef(count);
+ const ref = useRef();
if (firstRef === null) {
firstRef = ref;
} else if (firstRef !== ref) {
@@ -528,12 +526,12 @@ describe('ReactDOMServerHooks', () => {
yieldValue(count);
- return Count: {ref.current};
+ return Count: {count};
}
const domNode = await render();
expect(clearYields()).toEqual([0, 1, 2, 3]);
- expect(domNode.textContent).toEqual('Count: 0');
+ expect(domNode.textContent).toEqual('Count: 3');
},
);
});
diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js
index bdd0bed4c759d..231b33e69bcf9 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.new.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.new.js
@@ -27,6 +27,7 @@ import {
enableNewReconciler,
decoupleUpdatePriorityFromScheduler,
enableDoubleInvokingEffects,
+ enableUseRefAccessWarning,
} from 'shared/ReactFeatureFlags';
import {
@@ -1197,14 +1198,92 @@ function pushEffect(tag, create, destroy, deps) {
return effect;
}
+let stackContainsErrorMessage: boolean | null = null;
+
+function getCallerStackFrame(): string {
+ const stackFrames = new Error('Error message').stack.split('\n');
+
+ // Some browsers (e.g. Chrome) include the error message in the stack
+ // but others (e.g. Firefox) do not.
+ if (stackContainsErrorMessage === null) {
+ stackContainsErrorMessage = stackFrames[0].includes('Error message');
+ }
+
+ return stackContainsErrorMessage
+ ? stackFrames.slice(3, 4).join('\n')
+ : stackFrames.slice(2, 3).join('\n');
+}
+
function mountRef(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
- const ref = {current: initialValue};
- if (__DEV__) {
- Object.seal(ref);
+ if (enableUseRefAccessWarning) {
+ if (__DEV__) {
+ // Support lazy initialization pattern shown in docs.
+ // We need to store the caller stack frame so that we don't warn on subsequent renders.
+ let hasBeenInitialized = initialValue != null;
+ let lazyInitGetterStack = null;
+ let didCheckForLazyInit = false;
+
+ // Only warn once per component+hook.
+ let didWarnAboutRead = false;
+ let didWarnAboutWrite = false;
+
+ let current = initialValue;
+ const ref = {
+ get current() {
+ if (!hasBeenInitialized) {
+ didCheckForLazyInit = true;
+ lazyInitGetterStack = getCallerStackFrame();
+ } else if (currentlyRenderingFiber !== null && !didWarnAboutRead) {
+ if (
+ lazyInitGetterStack === null ||
+ lazyInitGetterStack !== getCallerStackFrame()
+ ) {
+ didWarnAboutRead = true;
+ console.warn(
+ '%s: Unsafe read of a mutable value during render.\n\n' +
+ 'Reading from a ref during render is only safe if:\n' +
+ '1. The ref value has not been updated, or\n' +
+ '2. The ref holds a lazily-initialized value that is only set once.\n',
+ getComponentName(currentlyRenderingFiber.type) || 'Unknown',
+ );
+ }
+ }
+ return current;
+ },
+ set current(value) {
+ if (currentlyRenderingFiber !== null && !didWarnAboutWrite) {
+ if (
+ hasBeenInitialized ||
+ (!hasBeenInitialized && !didCheckForLazyInit)
+ ) {
+ didWarnAboutWrite = true;
+ console.warn(
+ '%s: Unsafe write of a mutable value during render.\n\n' +
+ 'Writing to a ref during render is only safe if the ref holds ' +
+ 'a lazily-initialized value that is only set once.\n',
+ getComponentName(currentlyRenderingFiber.type) || 'Unknown',
+ );
+ }
+ }
+
+ hasBeenInitialized = true;
+ current = value;
+ },
+ };
+ Object.seal(ref);
+ hook.memoizedState = ref;
+ return ref;
+ } else {
+ const ref = {current: initialValue};
+ hook.memoizedState = ref;
+ return ref;
+ }
+ } else {
+ const ref = {current: initialValue};
+ hook.memoizedState = ref;
+ return ref;
}
- hook.memoizedState = ref;
- return ref;
}
function updateRef(initialValue: T): {|current: T|} {
@@ -1591,24 +1670,24 @@ function startTransition(setPending, callback) {
function mountTransition(): [(() => void) => void, boolean] {
const [isPending, setPending] = mountState(false);
- // The `start` method can be stored on a ref, since `setPending`
- // never changes.
+ // The `start` method never changes.
const start = startTransition.bind(null, setPending);
- mountRef(start);
+ const hook = mountWorkInProgressHook();
+ hook.memoizedState = start;
return [start, isPending];
}
function updateTransition(): [(() => void) => void, boolean] {
const [isPending] = updateState(false);
- const startRef = updateRef();
- const start: (() => void) => void = (startRef.current: any);
+ const hook = updateWorkInProgressHook();
+ const start = hook.memoizedState;
return [start, isPending];
}
function rerenderTransition(): [(() => void) => void, boolean] {
const [isPending] = rerenderState(false);
- const startRef = updateRef();
- const start: (() => void) => void = (startRef.current: any);
+ const hook = updateWorkInProgressHook();
+ const start = hook.memoizedState;
return [start, isPending];
}
diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js
index 972a746402786..ccd69abae6aab 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.old.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.old.js
@@ -26,6 +26,7 @@ import {
enableSchedulingProfiler,
enableNewReconciler,
decoupleUpdatePriorityFromScheduler,
+ enableUseRefAccessWarning,
} from 'shared/ReactFeatureFlags';
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
@@ -1175,14 +1176,92 @@ function pushEffect(tag, create, destroy, deps) {
return effect;
}
+let stackContainsErrorMessage: boolean | null = null;
+
+function getCallerStackFrame(): string {
+ const stackFrames = new Error('Error message').stack.split('\n');
+
+ // Some browsers (e.g. Chrome) include the error message in the stack
+ // but others (e.g. Firefox) do not.
+ if (stackContainsErrorMessage === null) {
+ stackContainsErrorMessage = stackFrames[0].includes('Error message');
+ }
+
+ return stackContainsErrorMessage
+ ? stackFrames.slice(3, 4).join('\n')
+ : stackFrames.slice(2, 3).join('\n');
+}
+
function mountRef(initialValue: T): {|current: T|} {
const hook = mountWorkInProgressHook();
- const ref = {current: initialValue};
- if (__DEV__) {
- Object.seal(ref);
+ if (enableUseRefAccessWarning) {
+ if (__DEV__) {
+ // Support lazy initialization pattern shown in docs.
+ // We need to store the caller stack frame so that we don't warn on subsequent renders.
+ let hasBeenInitialized = initialValue != null;
+ let lazyInitGetterStack = null;
+ let didCheckForLazyInit = false;
+
+ // Only warn once per component+hook.
+ let didWarnAboutRead = false;
+ let didWarnAboutWrite = false;
+
+ let current = initialValue;
+ const ref = {
+ get current() {
+ if (!hasBeenInitialized) {
+ didCheckForLazyInit = true;
+ lazyInitGetterStack = getCallerStackFrame();
+ } else if (currentlyRenderingFiber !== null && !didWarnAboutRead) {
+ if (
+ lazyInitGetterStack === null ||
+ lazyInitGetterStack !== getCallerStackFrame()
+ ) {
+ didWarnAboutRead = true;
+ console.warn(
+ '%s: Unsafe read of a mutable value during render.\n\n' +
+ 'Reading from a ref during render is only safe if:\n' +
+ '1. The ref value has not been updated, or\n' +
+ '2. The ref holds a lazily-initialized value that is only set once.\n',
+ getComponentName(currentlyRenderingFiber.type) || 'Unknown',
+ );
+ }
+ }
+ return current;
+ },
+ set current(value) {
+ if (currentlyRenderingFiber !== null && !didWarnAboutWrite) {
+ if (
+ hasBeenInitialized ||
+ (!hasBeenInitialized && !didCheckForLazyInit)
+ ) {
+ didWarnAboutWrite = true;
+ console.warn(
+ '%s: Unsafe write of a mutable value during render.\n\n' +
+ 'Writing to a ref during render is only safe if the ref holds ' +
+ 'a lazily-initialized value that is only set once.\n',
+ getComponentName(currentlyRenderingFiber.type) || 'Unknown',
+ );
+ }
+ }
+
+ hasBeenInitialized = true;
+ current = value;
+ },
+ };
+ Object.seal(ref);
+ hook.memoizedState = ref;
+ return ref;
+ } else {
+ const ref = {current: initialValue};
+ hook.memoizedState = ref;
+ return ref;
+ }
+ } else {
+ const ref = {current: initialValue};
+ hook.memoizedState = ref;
+ return ref;
}
- hook.memoizedState = ref;
- return ref;
}
function updateRef(initialValue: T): {|current: T|} {
@@ -1534,24 +1613,24 @@ function startTransition(setPending, callback) {
function mountTransition(): [(() => void) => void, boolean] {
const [isPending, setPending] = mountState(false);
- // The `start` method can be stored on a ref, since `setPending`
- // never changes.
+ // The `start` method never changes.
const start = startTransition.bind(null, setPending);
- mountRef(start);
+ const hook = mountWorkInProgressHook();
+ hook.memoizedState = start;
return [start, isPending];
}
function updateTransition(): [(() => void) => void, boolean] {
const [isPending] = updateState(false);
- const startRef = updateRef();
- const start: (() => void) => void = (startRef.current: any);
+ const hook = updateWorkInProgressHook();
+ const start = hook.memoizedState;
return [start, isPending];
}
function rerenderTransition(): [(() => void) => void, boolean] {
const [isPending] = rerenderState(false);
- const startRef = updateRef();
- const start: (() => void) => void = (startRef.current: any);
+ const hook = updateWorkInProgressHook();
+ const start = hook.memoizedState;
return [start, isPending];
}
diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
index 333e8930e7340..79794bbea7ff4 100644
--- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js
@@ -1536,7 +1536,7 @@ describe('ReactHooksWithNoopRenderer', () => {
it('does not show a warning when a component updates a childs state from within passive unmount function', () => {
function Parent() {
Scheduler.unstable_yieldValue('Parent');
- const updaterRef = React.useRef(null);
+ const updaterRef = useRef(null);
React.useEffect(() => {
Scheduler.unstable_yieldValue('Parent passive create');
return () => {
@@ -2612,7 +2612,7 @@ describe('ReactHooksWithNoopRenderer', () => {
});
// @gate new
- it('should skip unmounted boundaries and use the nearest still-mounted boundary', () => {
+ it('should skip unmounted boundaries and use the nearest still-mounted boundary', () => {
function Conditional({showChildren}) {
if (showChildren) {
return (
@@ -3202,91 +3202,6 @@ describe('ReactHooksWithNoopRenderer', () => {
});
});
- describe('useRef', () => {
- it('creates a ref object initialized with the provided value', () => {
- jest.useFakeTimers();
-
- function useDebouncedCallback(callback, ms, inputs) {
- const timeoutID = useRef(-1);
- useEffect(() => {
- return function unmount() {
- clearTimeout(timeoutID.current);
- };
- }, []);
- const debouncedCallback = useCallback(
- (...args) => {
- clearTimeout(timeoutID.current);
- timeoutID.current = setTimeout(callback, ms, ...args);
- },
- [callback, ms],
- );
- return useCallback(debouncedCallback, inputs);
- }
-
- let ping;
- function App() {
- ping = useDebouncedCallback(
- value => {
- Scheduler.unstable_yieldValue('ping: ' + value);
- },
- 100,
- [],
- );
- return null;
- }
-
- act(() => {
- ReactNoop.render();
- });
- expect(Scheduler).toHaveYielded([]);
-
- ping(1);
- ping(2);
- ping(3);
-
- expect(Scheduler).toHaveYielded([]);
-
- jest.advanceTimersByTime(100);
-
- expect(Scheduler).toHaveYielded(['ping: 3']);
-
- ping(4);
- jest.advanceTimersByTime(20);
- ping(5);
- ping(6);
- jest.advanceTimersByTime(80);
-
- expect(Scheduler).toHaveYielded([]);
-
- jest.advanceTimersByTime(20);
- expect(Scheduler).toHaveYielded(['ping: 6']);
- });
-
- it('should return the same ref during re-renders', () => {
- function Counter() {
- const ref = useRef('val');
- const [count, setCount] = useState(0);
- const [firstRef] = useState(ref);
-
- if (firstRef !== ref) {
- throw new Error('should never change');
- }
-
- if (count < 3) {
- setCount(count + 1);
- }
-
- return ;
- }
-
- ReactNoop.render();
- expect(Scheduler).toFlushAndYield(['val']);
-
- ReactNoop.render();
- expect(Scheduler).toFlushAndYield(['val']);
- });
- });
-
describe('useImperativeHandle', () => {
it('does not update when deps are the same', () => {
const INCREMENT = 'INCREMENT';
diff --git a/packages/react-reconciler/src/__tests__/useRef-test.internal.js b/packages/react-reconciler/src/__tests__/useRef-test.internal.js
new file mode 100644
index 0000000000000..ccfc6d0d4b2ae
--- /dev/null
+++ b/packages/react-reconciler/src/__tests__/useRef-test.internal.js
@@ -0,0 +1,364 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ * @jest-environment node
+ */
+
+/* eslint-disable no-func-assign */
+
+'use strict';
+
+describe('useRef', () => {
+ let React;
+ let ReactNoop;
+ let Scheduler;
+ let act;
+ let useCallback;
+ let useEffect;
+ let useLayoutEffect;
+ let useRef;
+ let useState;
+
+ beforeEach(() => {
+ React = require('react');
+ ReactNoop = require('react-noop-renderer');
+ Scheduler = require('scheduler');
+
+ const ReactFeatureFlags = require('shared/ReactFeatureFlags');
+ ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
+
+ act = ReactNoop.act;
+ useCallback = React.useCallback;
+ useEffect = React.useEffect;
+ useLayoutEffect = React.useLayoutEffect;
+ useRef = React.useRef;
+ useState = React.useState;
+ });
+
+ function Text(props) {
+ Scheduler.unstable_yieldValue(props.text);
+ return ;
+ }
+
+ it('creates a ref object initialized with the provided value', () => {
+ jest.useFakeTimers();
+
+ function useDebouncedCallback(callback, ms, inputs) {
+ const timeoutID = useRef(-1);
+ useEffect(() => {
+ return function unmount() {
+ clearTimeout(timeoutID.current);
+ };
+ }, []);
+ const debouncedCallback = useCallback(
+ (...args) => {
+ clearTimeout(timeoutID.current);
+ timeoutID.current = setTimeout(callback, ms, ...args);
+ },
+ [callback, ms],
+ );
+ return useCallback(debouncedCallback, inputs);
+ }
+
+ let ping;
+ function App() {
+ ping = useDebouncedCallback(
+ value => {
+ Scheduler.unstable_yieldValue('ping: ' + value);
+ },
+ 100,
+ [],
+ );
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ expect(Scheduler).toHaveYielded([]);
+
+ ping(1);
+ ping(2);
+ ping(3);
+
+ expect(Scheduler).toHaveYielded([]);
+
+ jest.advanceTimersByTime(100);
+
+ expect(Scheduler).toHaveYielded(['ping: 3']);
+
+ ping(4);
+ jest.advanceTimersByTime(20);
+ ping(5);
+ ping(6);
+ jest.advanceTimersByTime(80);
+
+ expect(Scheduler).toHaveYielded([]);
+
+ jest.advanceTimersByTime(20);
+ expect(Scheduler).toHaveYielded(['ping: 6']);
+ });
+
+ it('should return the same ref during re-renders', () => {
+ function Counter() {
+ const ref = useRef('val');
+ const [count, setCount] = useState(0);
+ const [firstRef] = useState(ref);
+
+ if (firstRef !== ref) {
+ throw new Error('should never change');
+ }
+
+ if (count < 3) {
+ setCount(count + 1);
+ }
+
+ return ;
+ }
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield([3]);
+
+ ReactNoop.render();
+ expect(Scheduler).toFlushAndYield([3]);
+ });
+
+ if (__DEV__) {
+ it('should never warn when attaching to children', () => {
+ class Component extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ function Example({phase}) {
+ const hostRef = useRef();
+ const classRef = useRef();
+ return (
+ <>
+
+
+ >
+ );
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ // @gate enableUseRefAccessWarning
+ it('should warn about reads during render', () => {
+ function Example() {
+ const ref = useRef(123);
+ let value;
+ expect(() => {
+ value = ref.current;
+ }).toWarnDev([
+ 'Example: Unsafe read of a mutable value during render.',
+ ]);
+ return value;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ it('should not warn about lazy init during render', () => {
+ function Example() {
+ const ref1 = useRef(null);
+ const ref2 = useRef(undefined);
+ // Read: safe because lazy init:
+ if (ref1.current === null) {
+ ref1.current = 123;
+ }
+ if (ref2.current === undefined) {
+ ref2.current = 123;
+ }
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+
+ // Should not warn after an update either.
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ it('should not warn about lazy init outside of render', () => {
+ function Example() {
+ // eslint-disable-next-line no-unused-vars
+ const [didMount, setDidMount] = useState(false);
+ const ref1 = useRef(null);
+ const ref2 = useRef(undefined);
+ useLayoutEffect(() => {
+ ref1.current = 123;
+ ref2.current = 123;
+ setDidMount(true);
+ }, []);
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ // @gate enableUseRefAccessWarning
+ it('should warn about unconditional lazy init during render', () => {
+ function Example() {
+ const ref1 = useRef(null);
+ const ref2 = useRef(undefined);
+
+ if (shouldExpectWarning) {
+ expect(() => {
+ ref1.current = 123;
+ }).toWarnDev([
+ 'Example: Unsafe write of a mutable value during render',
+ ]);
+ expect(() => {
+ ref2.current = 123;
+ }).toWarnDev([
+ 'Example: Unsafe write of a mutable value during render',
+ ]);
+ } else {
+ ref1.current = 123;
+ ref1.current = 123;
+ }
+
+ // But only warn once
+ ref1.current = 345;
+ ref1.current = 345;
+
+ return null;
+ }
+
+ let shouldExpectWarning = true;
+ act(() => {
+ ReactNoop.render();
+ });
+
+ // Should not warn again on update.
+ shouldExpectWarning = false;
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ // @gate enableUseRefAccessWarning
+ it('should warn about reads to ref after lazy init pattern', () => {
+ function Example() {
+ const ref1 = useRef(null);
+ const ref2 = useRef(undefined);
+
+ // Read 1: safe because lazy init:
+ if (ref1.current === null) {
+ ref1.current = 123;
+ }
+ if (ref2.current === undefined) {
+ ref2.current = 123;
+ }
+
+ let value;
+ expect(() => {
+ value = ref1.current;
+ }).toWarnDev(['Example: Unsafe read of a mutable value during render']);
+ expect(() => {
+ value = ref2.current;
+ }).toWarnDev(['Example: Unsafe read of a mutable value during render']);
+
+ // But it should only warn once.
+ value = ref1.current;
+ value = ref2.current;
+
+ return value;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ // @gate enableUseRefAccessWarning
+ it('should warn about writes to ref after lazy init pattern', () => {
+ function Example() {
+ const ref1 = useRef(null);
+ const ref2 = useRef(undefined);
+ // Read: safe because lazy init:
+ if (ref1.current === null) {
+ ref1.current = 123;
+ }
+ if (ref2.current === undefined) {
+ ref2.current = 123;
+ }
+
+ expect(() => {
+ ref1.current = 456;
+ }).toWarnDev([
+ 'Example: Unsafe write of a mutable value during render',
+ ]);
+ expect(() => {
+ ref2.current = 456;
+ }).toWarnDev([
+ 'Example: Unsafe write of a mutable value during render',
+ ]);
+
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+ });
+
+ it('should not warn about reads or writes within effect', () => {
+ function Example() {
+ const ref = useRef(123);
+ useLayoutEffect(() => {
+ expect(ref.current).toBe(123);
+ ref.current = 456;
+ expect(ref.current).toBe(456);
+ }, []);
+ useEffect(() => {
+ expect(ref.current).toBe(456);
+ ref.current = 789;
+ expect(ref.current).toBe(789);
+ }, []);
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+
+ ReactNoop.flushPassiveEffects();
+ });
+
+ it('should not warn about reads or writes outside of render phase (e.g. event handler)', () => {
+ let ref;
+ function Example() {
+ ref = useRef(123);
+ return null;
+ }
+
+ act(() => {
+ ReactNoop.render();
+ });
+
+ expect(ref.current).toBe(123);
+ ref.current = 456;
+ expect(ref.current).toBe(456);
+ });
+ }
+});
diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js
index 836b78524ef90..dbf9f5bd28533 100644
--- a/packages/shared/ReactFeatureFlags.js
+++ b/packages/shared/ReactFeatureFlags.js
@@ -134,3 +134,5 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+
+export const enableUseRefAccessWarning = false;
diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js
index 82e8d4dcd7baf..0df33470e5703 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-fb.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js
@@ -51,6 +51,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js
index 7a793d16ab330..77148d9be29b9 100644
--- a/packages/shared/forks/ReactFeatureFlags.native-oss.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
index 4402523c54105..7e66eb9e75e5e 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
index a71ad45dbd46a..90c4360c6d469 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
index c60072346eab2..4dbd671409e36 100644
--- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js
index 90c1fa268b25c..c98063376f09d 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js
index 717199df1d53b..362569dfb01b4 100644
--- a/packages/shared/forks/ReactFeatureFlags.testing.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js
@@ -50,6 +50,7 @@ export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = true;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
index 103e8adc5c72b..d03ba3eadd41c 100644
--- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
+++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js
@@ -48,3 +48,4 @@ export const enableTrustedTypesIntegration = false;
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
export const enableDoubleInvokingEffects = false;
+export const enableUseRefAccessWarning = __VARIANT__;
diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js
index 58d6987495f92..f5861a2e336a5 100644
--- a/packages/shared/forks/ReactFeatureFlags.www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -28,6 +28,7 @@ export const {
enableDebugTracing,
skipUnmountedBoundaries,
enableDoubleInvokingEffects,
+ enableUseRefAccessWarning,
} = dynamicFeatureFlags;
// On WWW, __EXPERIMENTAL__ is used for a new modern build.