Los custom hooks nos permiten reutilizar lógica de nuestros componentes en otros componentes. Los custom hooks siempre deben tener la palabra use al principio, como los hooks que ya conocemos (useEffect, useState…), esto es porque React necesita detectar que hablamos de un custom hook.

Supongamos que tenemos el siguiente código que trae un imagen de una API de gatitos con un texto de subtitulo que le pasamos en la variable firstWord

export const getImage = async (firstWord) => {
  return fetch(`https://cataas.com/c/s/${firstWord}?json=true`)
    .then(res => res.json())
    .then(data => {
      const { url } = data
      return url
    })
}

Vamos a hacerlo un custom hook para poder reutilizar esta lógica en otros componentes. Para eso primero creamos la funcion y le pasamos el estado de la imagen, ya que lo trabajaremos en esa funcion

function useCatImage () {
  const [imageUrl, setImageUrl] = useState() 
}

Copiamos y pegamos lo que teniamos en la funcion getImage (ademas yo le agregue un metodo para que no nos de un error al cargarlo)

useEffect(() => {
    if (!fact) return
    const firstWord = (fact.split(' ')[0])
    fetch(`https://cataas.com/c/s/${firstWord}?json=true`)
      .then(res => res.json())
      .then(data => {
        const { url } = data
        setImageUrl(url)
      })
  }, [fact])

Nos damos cuenta que necesitamos si o si el fact para que funcione, asique se lo pasamos como argumento a la funcion

function useCatImage ({ fact }) {
  const [imageUrl, setImageUrl] = useState()
  useEffect(() => {
    if (!fact) return
    const firstWord = (fact.split(' ')[0])
    fetch(`https://cataas.com/c/s/${firstWord}?json=true`)
      .then(res => res.json())
      .then(data => {
        const { url } = data
        setImageUrl(url)
      })
  }, [fact])
}

Esta funcion tiene que devolvernos algo, en este caso nos devolverá el URL de la imagen que necesitamos para que nos aparezca. Entonces le daremos un return de la imageUrl, que estará actualizada gracias al setImageUrl

function useCatImage ({ fact }) {
  const [imageUrl, setImageUrl] = useState()
  useEffect(() => {
    if (!fact) return
    const firstWord = (fact.split(' ')[0])
    fetch(`https://cataas.com/c/s/${firstWord}?json=true`)
      .then(res => res.json())
      .then(data => {
        const { url } = data
        setImageUrl(url)
      })
  }, [fact])
  return { imageUrl }
}

Ahora, el estado de la imagen, la forma de hacer fetch y la actualizacion de la imagen se realizan todo en la misma funcion. Lo que acabamos de crear es lo que usualmente en programacion se llama “caja negra”, es decir, una funcion que no es necesario que sepamos que tiene adentro sino que lo mas importante es saber que nos devolverá. Este concepto lo volverás a retomar mucho cuando aprendas bases de datos (si deseas convertirte en fullstack).

En tu funcion App ahora podrás llamar a este hook asi

const { imageUrl } = useCatImage({ fact })

Otra de las ventajas de los custom hooks sobre las funciones normales (como las del primer ejemplo) es que en los custom hooks podemos llamar a otros hooks, como el del estado que vimos justamente en este ejemplo. Un tip: Generalmente los useEffect suelen ser custom hooks.