Estoy usando ganchos de reacción useEffect y verifico si un objeto ha cambiado y solo luego vuelvo a ejecutar el gancho.
Mi código se ve así.
const useExample = (apiOptions) => { const [data, updateData] = useState([]); useEffect(() => { const [data, updateData] = useState<any>([]); doSomethingCool(apiOptions).then(res => { updateData(response.data); }) }, [apiOptions]); return { data }; };Desafortunadamente, sigue funcionando ya que los objetos no se reconocen como iguales.
Creo que el siguiente es un ejemplo de por qué.
const objA = { method: 'GET' } const objB = { method: 'GET' } console.log(objA === objB) ¿Quizás ejecutar JSON.stringify(apiOptions) funciona?
apiOptions como valor de estado No estoy seguro de cómo está consumiendo el gancho personalizado, pero hacer que apiOptions un valor de estado usando useState debería funcionar bien. De esta manera, puede servirlo en su enlace personalizado como un valor de estado de la siguiente manera:
const [apiOptions, setApiOptions] = useState({ a: 1 }) const { data } = useExample(apiOptions) De esta manera, solo cambiará cuando use setApiOptions .
Ejemplo 1
import { useState, useEffect } from 'react'; const useExample = (apiOptions) => { const [data, updateData] = useState([]); useEffect(() => { console.log('effect triggered') }, [apiOptions]); return { data }; } export default function App() { const [apiOptions, setApiOptions] = useState({ a: 1 }) const { data } = useExample(apiOptions); const [somethingElse, setSomethingElse] = useState('default state') return <div> <button onClick={() => { setApiOptions({ a: 1 }) }}>change apiOptions</button> <button onClick={() => { setSomethingElse('state') }}> change something else to force rerender </button> </div>; } Puede escribir un useEffect comparable profundo como se describe aquí :
function deepCompareEquals(a, b){ // TODO: implement deep comparison here // something like lodash // return _.isEqual(a, b); } function useDeepCompareMemoize(value) { const ref = useRef() // it can be done by using useMemo as well // but useRef is rather cleaner and easier if (!deepCompareEquals(value, ref.current)) { ref.current = value } return ref.current } function useDeepCompareEffect(callback, dependencies) { useEffect( callback, dependencies.map(useDeepCompareMemoize) ) } Puede usarlo como si usara useEffect .
Acabo de encontrar una solución que funciona para mí.
Tienes que usar usePrevious() y _.isEqual() de Lodash. Dentro de useEffect() , pones una condición si las apiOptions anteriores son iguales a las apiOptions actuales . Si es cierto, no hagas nada . Si es falso updateData() .
Ejemplo :
const useExample = (apiOptions) => { const myPreviousState = usePrevious(apiOptions); const [data, updateData] = useState([]); useEffect(() => { if (myPreviousState && !_.isEqual(myPreviousState, apiOptions)) { updateData(apiOptions); } }, [apiOptions]) } usePrevious(value) es un gancho personalizado que crea una ref con useRef() .
Puede encontrarlo en la documentación oficial de React Hook .
const usePrevious = value => { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; };Si la entrada es lo suficientemente superficial como para pensar que la igualdad profunda aún sería rápida, considere usar JSON.stringify :
const useExample = (apiOptions) => { const [data, updateData] = useState([]); const apiOptionsJsonString = JSON.stringify(apiOptions); useEffect(() => { const apiOptionsObject = JSON.parse(apiOptionsJsonString); doSomethingCool(apiOptionsObject).then(response => { updateData(response.data); }) }, [apiOptionsJsonString]); return { data }; };Tenga en cuenta que no comparará funciones.