@samitouri / QOS-React-2 / commits / fc47cb1b61

Fix suppressHydrationWarning not working in production (#24271)

dan committed Apr 4, 2022 at 16:23 UTC fc47cb1b61ac012f9bd6d7251eb19fcecf364a3b
3 files changed +101 -11
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+92
@@ -2811,4 +2811,96 @@ describe('ReactDOMFizzServer', () => {
2811 </ul>,
2812 );
2813 });
2814 +
2815 + // @gate experimental
2816 + it('suppresses and fixes text mismatches with suppressHydrationWarning', async () => {
2817 + function App({isClient}) {
2818 + return (
2819 + <div>
2820 + <span
2821 + suppressHydrationWarning={true}
2822 + data-attr={isClient ? 'client-attr' : 'server-attr'}>
2823 + {isClient ? 'Client Text' : 'Server Text'}
2824 + </span>
2825 + <span suppressHydrationWarning={true}>{isClient ? 2 : 1}</span>
2826 + <span suppressHydrationWarning={true}>
2827 + hello,{isClient ? 'client' : 'server'}
2828 + </span>
2829 + </div>
2830 + );
2831 + }
2832 + await act(async () => {
2833 + const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
2834 + <App isClient={false} />,
2835 + );
2836 + pipe(writable);
2837 + });
2838 + expect(getVisibleChildren(container)).toEqual(
2839 + <div>
2840 + <span data-attr="server-attr">Server Text</span>
2841 + <span>1</span>
2842 + <span>
2843 + {'hello,'}
2844 + {'server'}
2845 + </span>
2846 + </div>,
2847 + );
2848 + ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
2849 + onRecoverableError(error) {
2850 + // Don't miss a hydration error. There should be none.
2851 + Scheduler.unstable_yieldValue(error.message);
2852 + },
2853 + });
2854 + expect(Scheduler).toFlushAndYield([]);
2855 + // The text mismatch should be *silently* fixed. Even in production.
2856 + // The attribute mismatch should be ignored and not fixed.
2857 + expect(getVisibleChildren(container)).toEqual(
2858 + <div>
2859 + <span data-attr="server-attr">Client Text</span>
2860 + <span>2</span>
2861 + <span>
2862 + {'hello,'}
2863 + {'client'}
2864 + </span>
2865 + </div>,
2866 + );
2867 + });
2868 +
2869 + // @gate experimental
2870 + it('suppresses and does not fix html mismatches with suppressHydrationWarning', async () => {
2871 + function App({isClient}) {
2872 + return (
2873 + <div>
2874 + <p
2875 + suppressHydrationWarning={true}
2876 + dangerouslySetInnerHTML={{
2877 + __html: isClient ? 'Client HTML' : 'Server HTML',
2878 + }}
2879 + />
2880 + </div>
2881 + );
2882 + }
2883 + await act(async () => {
2884 + const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
2885 + <App isClient={false} />,
2886 + );
2887 + pipe(writable);
2888 + });
2889 + expect(getVisibleChildren(container)).toEqual(
2890 + <div>
2891 + <p>Server HTML</p>
2892 + </div>,
2893 + );
2894 + ReactDOMClient.hydrateRoot(container, <App isClient={true} />, {
2895 + onRecoverableError(error) {
2896 + Scheduler.unstable_yieldValue(error.message);
2897 + },
2898 + });
2899 + expect(Scheduler).toFlushAndYield([]);
2900 + expect(getVisibleChildren(container)).toEqual(
2901 + <div>
2902 + <p>Server HTML</p>
2903 + </div>,
2904 + );
2905 + });
2906 });
packages/react-dom/src/client/ReactDOMComponent.js
+8 -7
@@ -91,7 +91,6 @@ const STYLE = 'style';
91 const HTML = '__html';
92
93 let warnedUnknownTags;
94 -let suppressHydrationWarning;
94
95 let validatePropertiesInDevelopment;
96 let warnForPropDifference;
@@ -875,7 +874,6 @@ export function diffHydratedProperties(
874 let extraAttributeNames: Set<string>;
875
876 if (__DEV__) {
878 - suppressHydrationWarning = rawProps[SUPPRESS_HYDRATION_WARNING] === true;
877 isCustomComponentTag = isCustomComponent(tag, rawProps);
878 validatePropertiesInDevelopment(tag, rawProps);
879 }
@@ -984,7 +982,7 @@ export function diffHydratedProperties(
982 // TODO: Should we use domElement.firstChild.nodeValue to compare?
983 if (typeof nextProp === 'string') {
984 if (domElement.textContent !== nextProp) {
987 - if (!suppressHydrationWarning) {
985 + if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
986 checkForUnmatchedText(
987 domElement.textContent,
988 nextProp,
@@ -996,7 +994,7 @@ export function diffHydratedProperties(
994 }
995 } else if (typeof nextProp === 'number') {
996 if (domElement.textContent !== '' + nextProp) {
999 - if (!suppressHydrationWarning) {
997 + if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
998 checkForUnmatchedText(
999 domElement.textContent,
1000 nextProp,
@@ -1028,7 +1026,7 @@ export function diffHydratedProperties(
1026 isCustomComponentTag && enableCustomElementPropertySupport
1027 ? null
1028 : getPropertyInfo(propKey);
1031 - if (suppressHydrationWarning) {
1029 + if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) {
1030 // Don't bother comparing. We're ignoring all these warnings.
1031 } else if (
1032 propKey === SUPPRESS_CONTENT_EDITABLE_WARNING ||
@@ -1150,8 +1148,11 @@ export function diffHydratedProperties(
1148
1149 if (__DEV__) {
1150 if (shouldWarnDev) {
1153 - // $FlowFixMe - Should be inferred as not undefined.
1154 - if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
1151 + if (
1152 + // $FlowFixMe - Should be inferred as not undefined.
1153 + extraAttributeNames.size > 0 &&
1154 + rawProps[SUPPRESS_HYDRATION_WARNING] !== true
1155 + ) {
1156 // $FlowFixMe - Should be inferred as not undefined.
1157 warnForExtraAttributes(extraAttributeNames);
1158 }
packages/react-dom/src/client/ReactDOMHostConfig.js
+1 -4
@@ -132,10 +132,7 @@ type SelectionInformation = {|
132 selectionRange: mixed,
133 |};
134
135 -let SUPPRESS_HYDRATION_WARNING;
136 -if (__DEV__) {
137 - SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
138 -}
135 +const SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
136
137 const SUSPENSE_START_DATA = '$';
138 const SUSPENSE_END_DATA = '/$';