Al crear un componente de función en React y establecer un parámetro predeterminado, todo funciona como se esperaba y el componente se representará una vez. Pero tan pronto como agrega un enlace como useEffect y usa este parámetro en la matriz de dependencia, el componente se vuelve a representar para siempre. Creé una demostración simple aquí: https://codesandbox.io/s/infinite-useeffect-loop-on-default-value-tv7hj?file=/src/TestComponent.jsx
La razón es bastante obvia, ya que al usar un objeto como parámetro por defecto, se volverá a crear y no será igual al anterior. Y, por supuesto, esto no sucede en valores primitivos de parámetros predeterminados como número o cadena.
¿Hay alguna forma mejor de evitar este efecto secundario además de usar defaultProps ?
Sí, en lugar de establecer el valor predeterminado de value para que sea un objeto, simplemente configúrelo en falso. Luego verifique si el value es verdadero, si lo es, luego acceda a las propiedades correctas, de lo contrario, solo muestre un valor predeterminado. Código nuevo .
Sería algo como:
import { useEffect, useState } from "react"; const TestComponent = ({ value = false }) => { const [calcValue, setCalcValue] = useState(0); useEffect(() => { setCalcValue((cur) => cur + 1); }, [value]); return ( <div> {value ? value.name : "Test"}:{calcValue} </div> ); };La razón por la que obtiene bucles infinitos es porque la referencia de value sigue cambiando.
La primera vez que se procesa el componente, ve una nueva referencia al valor, lo que activa el useEffect , que a su vez modifica el estado del componente, y esto conduce a un nuevo procesamiento, lo que hace que el value se vuelva a crear porque la antigua referencia a esa variable ha cambiado.
La forma más fácil de lidiar con esto es simplemente crear un valor predeterminado fuera del componente y usarlo ( básicamente lo mismo que la solución defaultProps ):
import { useEffect, useState } from "react"; const defaultValue = {name: "Test"}; // <-- default here const TestComponent = ({ value = defaultValue }) => { const [calcValue, setCalcValue] = useState(0); useEffect(() => { setCalcValue((cur) => cur + 1); }, [value]); return ( <div> {value.name}:{calcValue} </div> ); }; Hacer esto asegurará que cada vez que el componente se represente, vea la misma referencia para value , por lo tanto, el useEffect solo se ejecuta una vez.
Otra forma de lidiar con esto es primero envolver su componente con memo , luego crear una nueva variable de estado que tome el value original y hacer que su useEffect dependa de esta nueva variable de estado:
const TestComponent = React.memo(({ value = {name: "Test"} }) => { const [calcValue, setCalcValue] = useState(0); const [myValue, setMyValue] = useState(value); useEffect(() => { setCalcValue((cur) => cur + 1); }, [myValue]); return ( <div> {myValue.name}:{calcValue} </div> ); }); La razón por la que envolvemos el componente con memo es para que solo se vuelva a renderizar después de un cambio de estado si el valor de la propiedad ha cambiado (en lugar de la referencia ). Puede cambiar la forma en memo detecta los cambios de accesorios proporcionando una función de comparación personalizada como segundo parámetro.