Estoy muy confundido acerca de la salida del siguiente componente:
import { StrictMode } from "react" import ReactDOM from "react-dom" function Test(): React.ReactElement { console.log('render') Promise.resolve() .then(() => console.log('then ' + Math.random())) return <></> } ReactDOM.render( <StrictMode> <Test /> </StrictMode>, document.getElementById("root") )
Produce el siguiente resultado al menos en Chrome y Firefox:
00:46:30.264 render 00:46:30.267 then 0.5430663800781927 00:46:30.267 then 0.9667426372511254
Prefiero esperar ver el mismo número de mensajes. ¿Qué me estoy perdiendo?
Reproducción: https://codesandbox.io/s/elegant-frost-dmcsl
EDITAR: sé que el modo estricto conduce a una representación adicional, pero como se indicó, esperaría el mismo recuento de mensajes.
EDIT 2: Ambas respuestas a continuación son geniales. Me gustaría citar el comentario de @user56reinstatemonica8 aquí:
Relevante: comentarios de la comunidad sobre el silenciamiento de la consola
En el modo estricto de Reacción, la reacción puede ejecutar el procesamiento varias veces, lo que podría explicar en parte lo que ve.
Pero se preguntó correctamente si ese era el caso y el renderizado se llamó varias veces, ¿por qué el render
no se imprimió dos veces también ?
React modifica los métodos de la consola como console.log()
para silenciar los registros en algunos casos. Aquí hay una cita:
A partir de React 17, React modifica automáticamente los métodos de la consola como console.log() para silenciar los registros en la segunda llamada a las funciones del ciclo de vida. Sin embargo, puede causar un comportamiento no deseado en ciertos casos en los que se puede utilizar una solución alternativa.
Aparentemente, no lo hace cuando se llama a console.log
desde la devolución de llamada de Promise. Pero lo hace cuando se llama desde render. Más detalles sobre esto están en la respuesta de @trincot.
Hay una segunda ejecución de su función de procesamiento cuando el modo estricto está habilitado (solo en el modo de desarrollo), pero como se explica aquí , React usará los métodos de la console
parches (llamando a disableLogs();
) durante la duración de esa segunda ejecución (sincrónica), para que no salga.
El registro de cambios muestra que este código se insertó en packages/react-reconciler/src/ReactFiberBeginWork.js
para suprimir temporalmente los registros (inserciones marcadas con comentarios):
if (__DEV__) { ReactCurrentOwner.current = workInProgress; setIsRendering(true); nextChildren = renderWithHooks( current, workInProgress, render, nextProps, ref, renderExpirationTime, ); if ( debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode ) { disableLogs(); // <-- try { // <-- nextChildren = renderWithHooks( current, workInProgress, render, nextProps, ref, renderExpirationTime, ); } finally { // <-- reenableLogs(); // <-- } // <--
Aquí hay una versión de su código que demuestra que realmente se ejecuta dos veces:
var i = 0; var myconsolelog = console.log; // Work around React's monkeypatching function Test(): React.ReactElement { i++; myconsolelog(i + ". render"); // will output twice now! Promise.resolve(i) .then((i) => console.log(i + ". then " + Math.random())); return <></>; }
En mi opinión, esta supresión de registro es una elección de diseño realmente mala .
En caso de que pueda ayudar a alguien, puede aplicar un parche de mono a Object.defineProperties
para ignorar cualquier cambio realizado en el objeto de la console
, evitando efectivamente que ReactDOM aplique un parche de mono a console.log
.
const defineProperties = Object.defineProperties; Object.defineProperties = function (o, props) { return o === console ? o : defineProperties(o, props); };
Asegúrese de poner esto solo en modo de desarrollo (por ejemplo, en create-react-app cuando process.env.NODE_ENV === 'development'
), para que no termine en la compilación de producción.