Inicio ReactJS - parte 5
Post
Cancel

ReactJS - parte 5

Custom Hook

Los hooks personalizados son muy útiles para limpiar su código. Puedes usar hooks de React dentro de tus hooks personalizados (después de todo, todas son funciones! 👍). Puede encapsular mucha lógica repetitiva y luego devolver lo que necesita desde el hook personalizado.

components(Header.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
const logo = "./src/assets/react.svg";

const Header = () => {
    return (
        <>
            <h1 className="text-info text-center">Hooks en React</h1>
            <img className="rounded d-block mx-auto" src={logo} />
            <hr></hr>
        </>
    );
}

export default Header;

App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { useCallback, useEffect, useState } from "react";
import Header from "./components/Header";

const App = () => {
  const [data, setData] = useState([]);
  const [url, setUrl] = useState("");

  //https://jsonplaceholder.typicode.com/users

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(url)
  };

  const fetchData = useCallback(async () => {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw "Error al conectar la API";
      }
      const data = await response.json();
      setData(data);
    } catch (error) {
      console.log(error);
      setData([]);
    }
  }, [url]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);


  if (!data) return <p>Cargando datos...</p>

  return (
    <div className="container">
      <Header />
      <form
        onSubmit={handleSubmit}
      >
          <input
            className="form-control mb-2"
            type="text"
            placeholder="Ingrese un URL"
            name="url"
            value={url}
            onChange={(e) => setUrl(e.target.value)}
          />
      </form>
      <hr></hr>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

components/Form.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useState } from "react";
import { useFetch } from "../hooks/useFetch";

const Form = () => {
    const [url, setUrl] = useState("");
    const { data } = useFetch(url);

    //https://jsonplaceholder.typicode.com/users

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log(url)
    };

    return (
        <>
            <form
                onSubmit={handleSubmit}
            >
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un URL"
                    name="url"
                    value={url}
                    onChange={(e) => setUrl(e.target.value)}
                />
            </form>
            <h3 className="text-center">Resultados</h3>
            <hr></hr>
            <pre>{JSON.stringify(data, null, 2)}</pre>
        </>
    );
};

export default Form;

hooks/useFetch.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { useCallback, useEffect, useState } from "react";

export const useFetch = (url) => {
    const [data, setData] = useState([]);

    const fetchData = useCallback(async () => {
        try {
            const response = await fetch(url);
            if (!response.ok) {
                throw "Error al conectar la API";
            }
            const data = await response.json();
            setData(data);
        } catch (error) {
            console.log(error);
            setData([]);
        }
    }, [url]);

    useEffect(() => {
        fetchData();
    }, [fetchData]);

    return { data };
}

App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Header from "./components/Header";
import Form from "./components/Form";

const App = () => {

  //const { data } = useFetch("https://jsonplaceholder.typicode.com/albums");

  return (
    <div className="container">
      <Header />
      <Form />
    </div>
  );
};

export default App;

Manejo de errores

useFetch.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useCallback, useEffect, useState } from "react";

export const useFetch = (url) => {
    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState("");

    const fetchData = useCallback(async () => {
        setLoading(true);
        try {
            if (!url) {
                throw Error("No se ha especificado una URL");
            } else {
                const response = await fetch(url);
                if (!response.ok) {
                    throw Error("Error al conectar la API");
                }
                const data = await response.json();
                setData(data);
                setError("")
            }
        } catch (error) {
            console.log(error.message);
            setError(error.message);
            setData([]);
        } finally {
            setLoading(false);
        }
    }, [url]);

    useEffect(() => {
        fetchData();
    }, [fetchData]);

    return { data, loading, error };
}

Form.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { useState } from "react";
import { useFetch } from "../hooks/useFetch";

const Form = () => {
    const [url, setUrl] = useState('');
    const { data, loading, error } = useFetch(url);

    //https://jsonplaceholder.typicode.com/users

    const handleSubmit = (e) => {
        setUrl(e.target.url.value);
        e.preventDefault();
        console.log(url)
    };
    if (loading) return <p>Cargando...</p>
    return (
        <>
            <form
                onSubmit={handleSubmit}
            >
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un URL"
                    name="url"
                    value={url}
                    onChange={(e) => setUrl(e.target.value)}
                />
            </form>
            <hr></hr>
            { ( error ) ? <p className="badge rounded-pill bg-warning text-dark">{error} </p> :
                <pre>{JSON.stringify(data, null, 2)}</pre>
            }
        </>
    );
};

export default Form;

Syntax Highlighter

Form.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import { useState } from "react";
import { useFetch } from "../hooks/useFetch";
import SyntaxHighlighter from 'react-syntax-highlighter';
import { monokaiSublime } from 'react-syntax-highlighter/dist/esm/styles/hljs';

const Form = () => {
    const [url, setUrl] = useState('');
    const { data, loading, error } = useFetch(url);

    //https://jsonplaceholder.typicode.com/users

    const handleSubmit = (e) => {
        setUrl(e.target.url.value);
        e.preventDefault();
        console.log(url)
    };
    if (loading) return <p>Cargando...</p>
    return (
        <>
            <form
                onSubmit={handleSubmit}
            >
                <input
                    className="form-control mb-2"
                    type="text"
                    placeholder="Ingrese un URL"
                    name="url"
                    value={url}
                    onChange={(e) => setUrl(e.target.value)}
                />
            </form>
            <hr></hr>
            {(error) ? <p className="badge rounded-pill bg-warning text-dark"> {error} </p> :

                <SyntaxHighlighter language="json" style={monokaiSublime} showLineNumbers={true}>
                    {JSON.stringify(data, null, 2)}
                </SyntaxHighlighter>


            }
        </>
    );
};

export default Form;

Custom Hook useGif

1
2
3
4
5
6
7
8
9
10
11
12
13
const logo = "./src/assets/react.svg";

const Header = () => {
    return (
        <>
            <h1 className="text-info text-center">Hook personalizado en React</h1>
            <img className="rounded d-block mx-auto" src={logo} />
            <hr></hr>
        </>
    );
}

export default Header;
  • components/Random_v1.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { useState, useEffect } from 'react';
import axios from 'axios';

const API_KEY = 'szInMDHl8WSrGGDZjbNSbUNYX5DmC8Fq';

const Random = () => {
    const [gif, setGif] = useState('');

    const fetchGif = async () => {
        const url = `https://api.giphy.com/v1/gifs/random?api_key=${API_KEY}`;
        const { data } = await axios.get(url);
        const imageSrc = data.data.images.downsized_large.url;
        setGif(imageSrc);
    }

    useEffect(() => {
        fetchGif();
    }, []);

    const handleClick = () => {
        fetchGif();
    }

    return (
        <div className="col-sm-6 text-center">
            <h2 className="text-info">Random Gif v1</h2>
            <img width="500" src={gif} alt="Random Gif" />
            <br /><br />
            <button className='btn btn-info' onClick={handleClick}>Cargar nuevo Gif</button>
        </div>
    );
}

export default Random;
  • components/Tag_v1.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useState, useEffect } from 'react';
import axios from 'axios';

const API_KEY = 'szInMDHl8WSrGGDZjbNSbUNYX5DmC8Fq';

const Tag = () => {
    const [tag, setTag] = useState('cats');
    const [gif, setGif] = useState('');

    const fetchGif = async (tag) => {
        const url = `https://api.giphy.com/v1/gifs/random?api_key=${API_KEY}&tag=${tag}`;
        const { data } = await axios.get(url);
        const imageSrc = data.data.images.downsized_large.url;
        setGif(imageSrc);
    }

    useEffect(() => {
        fetchGif(tag);
    }, [tag]);

    const handleClick = () => {
        fetchGif(tag);
    }

    return (
        <div className="col-sm-6 text-center">
            <h2 className="text-info">Random Gif de { tag } v1</h2>
            <img width="500" src={gif} alt="Tag Gif" />
            <br /><br />    
            <input className="form-control mb-2" value={tag} onChange={(e) => setTag(e.target.value)} />
            <button className='btn btn-info' onClick={handleClick}>Buscar Gif</button>
        </div>
    );
}

export default Tag;
  • App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Header from "./components/Header";
import Random from "./components/Random_v1";
import Tag from "./components/Tag_v1";

const App = () => {

  return (
    <div className="container">
      <Header />
      <div className="row">
        <Random />
        <Tag />
      </div>
    </div>
  );
};

export default App;
  • Crear custom hook hooks/useGif.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { useState, useEffect } from 'react';
import axios from 'axios';

const API_KEY = 'szInMDHl8WSrGGDZjbNSbUNYX5DmC8Fq';

const url = `https://api.giphy.com/v1/gifs/random?api_key=${API_KEY}`;

const useGif = (tag) => {
    const [gif, setGif] = useState('');

    const fetchGif = async (tag) => {
        const { data } = await axios.get(tag ? `${url}&tag=${tag}` : url);
        const imageSrc = data.data.images.downsized_large.url;
        setGif(imageSrc);
    }

    useEffect(() => {
        fetchGif(tag);
    }, [tag]);

    return { gif, fetchGif }
}

export default useGif;
  • components/Random_v2.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import useGif from '../hooks/useGif';

const Tag = () => {
    const {gif, fetchGif} = useGif();

    return (
        <div className="col-sm-6 text-center">
            <h2 className="text-info">Random Gif v2</h2>
            <img width="500" src={gif} alt="Random Gif" />
            <br /><br />
            <button className='btn btn-info' onClick={fetchGif}>Cargar nuevo Gif</button>
        </div>
    );
}

export default Tag;
  • components/Tag_v2.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState } from 'react';
import useGif from '../hooks/useGif';

const Tag = () => {
    const [tag, setTag] = useState('cats');
    const {gif, fetchGif} = useGif(tag);

    return (
        <div className="col-sm-6 text-center">
            <h2 className="text-info">Random Gif de { tag } v2</h2>
            <img width="500" src={gif} alt="Random Gif" />
            <br /><br />
            <input className="form-control mb-2" value={tag} onChange={(e) => setTag(e.target.value)} />
            <button className='btn btn-info' onClick={() => fetchGif(tag)}>Buscar Gif</button>
        </div>
    );
}

export default Tag;
  • ⁠App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Header from "./components/Header";
import Random from "./components/Random_v2";
import Tag from "./components/Tag_v2";

const App = () => {

  return (
    <div className="container">
      <Header />
      <div className="row">
        <Random />
        <Tag />
      </div>
    </div>
  );
};

export default App;
This post is licensed under CC BY 4.0 by the author.