From 942e41ebd6488dc44e1810e49de8c5ed3dc22d1c Mon Sep 17 00:00:00 2001 From: Joice Doll Date: Sat, 18 Oct 2025 18:29:55 -0300 Subject: [PATCH] create reusable useFetch hook to simplify API calls and handle loading status --- src/about/About.js | 98 +++++++++++++++++++------------------- src/dashboard/Dashboard.js | 25 +++------- src/hooks/useFetch.js | 40 ++++++++++++++++ src/levels/Levels.js | 22 +++------ src/projects/Project.js | 48 +++++++------------ src/rules/Rule.js | 25 +++++----- src/rules/Rules.js | 22 +++------ 7 files changed, 137 insertions(+), 143 deletions(-) create mode 100644 src/hooks/useFetch.js diff --git a/src/about/About.js b/src/about/About.js index 8efd630..66792b2 100644 --- a/src/about/About.js +++ b/src/about/About.js @@ -4,58 +4,56 @@ import React from "react"; import { Box, Link, Typography } from "@material-ui/core"; -const About = () => { - return ( - - - About +const About = () => ( + + + About + + + An open source gitlab linting utility. + + + + + Contribute - - An open source gitlab linting utility. + + Fork the repository and send your pull-requests. +
    +
  • + Frontend: + + https://github.com/globocom/gitlab-lint-react + +
  • +
  • + Backend: + + https://github.com/globocom/gitlab-lint + +
  • +
+
- - - Contribute - - - Fork the repository and send your pull-requests. - -
    -
  • - Frontend: - - https://github.com/globocom/gitlab-lint-react - -
  • -
  • - Backend: - - https://github.com/globocom/gitlab-lint - -
  • -
-
- - - - License - - - By contributing to gitlab-lint, you agree that your contributions will - be licensed under its BSD 3-Clause license. - - -
- ); -}; + + + License + + + By contributing to gitlab-lint, you agree that your contributions will + be licensed under its BSD 3-Clause license. + + +
+); export default About; diff --git a/src/dashboard/Dashboard.js b/src/dashboard/Dashboard.js index ab735cc..30fac4b 100644 --- a/src/dashboard/Dashboard.js +++ b/src/dashboard/Dashboard.js @@ -1,30 +1,19 @@ // Copyright (c) 2021, Marcelo Jorge Vieira // Licensed under the BSD 3-Clause License -import React, { useState, useEffect } from "react"; - -import GitlabLintHttpClient from "../GitlabLintHttpClient"; +import React from "react"; +import useFetch from "../hooks/useFetch"; import Loading from "../Loading"; import Numbers from "./Numbers"; import Stats from "./Stats"; const Dashboard = () => { - const [rows, setData] = useState({}); - const fetchData = () => { - GitlabLintHttpClient("GET_ALL", { entity: "stats" }) - .then((data) => { - setData(data.data); - }) - .catch((err) => console.error(err)); - }; - - useEffect(() => { - fetchData(); - }, []); + const { data: rows, loading } = useFetch({ + method: "GET_ALL", + entity: "stats", + }); - if (Object.keys(rows).length === 0 && rows.constructor === Object) { - return ; - } + if (loading) return ; return ( diff --git a/src/hooks/useFetch.js b/src/hooks/useFetch.js new file mode 100644 index 0000000..3430c5e --- /dev/null +++ b/src/hooks/useFetch.js @@ -0,0 +1,40 @@ +import { useState, useEffect, useCallback } from "react"; +import GitlabLintHttpClient from "../GitlabLintHttpClient"; + +function useFetch({ method, entity, id = null, params = {} }) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchData = useCallback( + (overrideParams = {}) => { + setLoading(true); + setError(null); + + const payload = { + entity, + ...(id && { id }), + ...params, + ...overrideParams, + }; + + GitlabLintHttpClient(method, payload) + .then((res) => setData(res.data)) + .catch((err) => { + setError( + err.response?.data?.errors?._all || err.message || "Unknown error" + ); + }) + .finally(() => setLoading(false)); + }, + [method, entity, id, JSON.stringify(params)] + ); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + return { data, loading, error, refetch: fetchData }; +} + +export default useFetch; diff --git a/src/levels/Levels.js b/src/levels/Levels.js index 8c0c663..92c8908 100644 --- a/src/levels/Levels.js +++ b/src/levels/Levels.js @@ -1,8 +1,9 @@ // Copyright (c) 2021, Marcelo Jorge Vieira // Licensed under the BSD 3-Clause License -import React, { useState, useEffect } from "react"; +import React from "react"; import { makeStyles } from "@material-ui/core/styles"; +import useFetch from "../hooks/useFetch"; import { Card, CardContent, @@ -12,7 +13,6 @@ import { } from "@material-ui/core"; import Loading from "../Loading"; -import GitlabLintHttpClient from "../GitlabLintHttpClient"; import levelsStyle from "../theme"; const useStyles = makeStyles((theme) => ({ @@ -31,20 +31,12 @@ const useStyles = makeStyles((theme) => ({ const Levels = () => { const classes = useStyles(); - const [rows, setData] = useState({}); - const fetchData = () => { - GitlabLintHttpClient("GET_ALL", { entity: "levels" }) - .then((data) => { - setData(data.data); - }) - .catch((err) => console.error(err)); - }; + const { data: rows, loading } = useFetch({ + method: "GET_ALL", + entity: "levels", + }); - useEffect(() => { - fetchData(); - }, []); - - if (Object.keys(rows).length === 0 && rows.constructor === Object) { + if (loading) { return ; } diff --git a/src/projects/Project.js b/src/projects/Project.js index c622c67..9304131 100644 --- a/src/projects/Project.js +++ b/src/projects/Project.js @@ -1,7 +1,7 @@ // Copyright (c) 2021, Marcelo Jorge Vieira // Licensed under the BSD 3-Clause License -import React, { useState, useEffect, useCallback } from "react"; +import React from "react"; import { Link as RouterLink, useParams } from "react-router-dom"; import { Box, @@ -12,47 +12,33 @@ import { ListItem, Typography, } from "@material-ui/core"; +import Loading from "../Loading"; +import useFetch from "../hooks/useFetch"; -import GitlabLintHttpClient from "../GitlabLintHttpClient"; import RuleTitle from "../rules/RuleTitle"; import ProjectTitle from "./ProjectTitle"; const Project = () => { - const [rows, setData] = useState({}); - const [errorMessage, setErrorMessage] = useState({}); const { id } = useParams(); - const fetchData = useCallback(() => { - GitlabLintHttpClient("GET_ONE", { entity: "projects", id: id }) - .then((data) => { - setData(data.data); - }) - .catch((err) => { - setErrorMessage({ - status: err.response.status, - message: err.response.data.errors["_all"], - }); - console.error(err); - console.error(err.response); - }); - }, [id]); + const { + data: rows, + loading, + error, + } = useFetch({ + method: "GET_ONE", + entity: "projects", + id, + }); - useEffect(() => { - fetchData(); - }, [fetchData]); + if (loading) { + return ; + } - if (Object.keys(rows).length === 0 && rows.constructor === Object) { + if (error) { let messageTitle = "Error"; - if (errorMessage.status === 404) { + if (error.status === 404) { messageTitle = "Project not found"; } - return ( - <> - - {messageTitle} - -
{errorMessage.message}
- - ); } return ( diff --git a/src/rules/Rule.js b/src/rules/Rule.js index 724e096..8ae902e 100644 --- a/src/rules/Rule.js +++ b/src/rules/Rule.js @@ -1,10 +1,9 @@ // Copyright (c) 2021, Marcelo Jorge Vieira // Licensed under the BSD 3-Clause License -import React, { useState, useEffect, useCallback } from "react"; +import React from "react"; import { makeStyles } from "@material-ui/core/styles"; import { Link as RouterLink, useParams } from "react-router-dom"; - import { Box, Breadcrumbs, @@ -16,9 +15,9 @@ import { Typography, } from "@material-ui/core"; -import GitlabLintHttpClient from "../GitlabLintHttpClient"; import Loading from "../Loading"; import RuleTitle from "./RuleTitle"; +import useFetch from "../hooks/useFetch"; const useStyles = makeStyles((theme) => ({ ruleDescription: { @@ -62,19 +61,17 @@ const RuleProjects = ({ projects }) => { const Rule = () => { const classes = useStyles(); - const [rows, setData] = useState({}); const { id } = useParams(); - const fetchData = useCallback(() => { - GitlabLintHttpClient("GET_ONE", { entity: "rules", id: id }) - .then((data) => { - setData(data.data); - }) - .catch((err) => console.error(err)); - }, [id]); - useEffect(() => { - fetchData(); - }, [fetchData]); + const { data: rows, loading } = useFetch({ + method: "GET_ONE", + entity: "rules", + id, + }); + + if (loading) { + return ; + } if (Object.keys(rows).length === 0 && rows.constructor === Object) { return ; diff --git a/src/rules/Rules.js b/src/rules/Rules.js index 281a081..36fb1dd 100644 --- a/src/rules/Rules.js +++ b/src/rules/Rules.js @@ -1,7 +1,7 @@ // Copyright (c) 2021, Marcelo Jorge Vieira // Licensed under the BSD 3-Clause License -import React, { useState, useEffect } from "react"; +import React from "react"; import { Link } from "react-router-dom"; import { makeStyles } from "@material-ui/core/styles"; import { @@ -15,9 +15,9 @@ import { Typography, } from "@material-ui/core"; -import GitlabLintHttpClient from "../GitlabLintHttpClient"; import Loading from "../Loading"; import levelsStyles from "../theme"; +import useFetch from "../hooks/useFetch"; const useStyles = makeStyles((theme) => ({ root: { @@ -31,20 +31,12 @@ const useStyles = makeStyles((theme) => ({ const Rules = () => { const classes = useStyles(); - const [rows, setData] = useState({}); - const fetchData = () => { - GitlabLintHttpClient("GET_ALL", { entity: "rules" }) - .then((data) => { - setData(data.data); - }) - .catch((err) => console.error(err)); - }; + const { data: rows, loading } = useFetch({ + method: "GET_ALL", + entity: "rules", + }); - useEffect(() => { - fetchData(); - }, []); - - if (Object.keys(rows).length === 0 && rows.constructor === Object) { + if (loading) { return ; }