This project allows you to search on github by users / repos using Github Apis.
In the project, I aimed at having it structured in a feature based structure, in which each feature is modular with its own components types, resources, redux-slice, context (to avoid props drilling while maintaining the usage of React Hooks), tests with having the app folder for the shared resources mentioned above. This structure makes sure each module is a stand-alone application yet still provide ability for inheritance and exposure to other modules, a structure that can be scaled very well or even suit a micro-frontend approach - if any - in the future well.
Since we use redux, it has a big role in the structure, almost every module is going to use redux. hence, have its own redux-slice
, so we will have a redux
directory for the slice, the apis functions, and the services to have the thunk actions in a separate file for more cleanness of the code.
We have a resources
dir, which includes anything that's not a page, redux, component e.g. Context, hooks, types, utils ...etc.
We have the components and tests files which contain what their title says for their respective modules.
the index file is the page file of this module, if we have multiple pages we can either create pages folder if they are on the same level or nest modules in case of them having parent - children relationship e.g.
-> products
---> [productId]
-> search
right here the products and the search are on the same level but the productId dynamic route is a child module of our products module.
we can have cases in which they all are separated in different modules which means they have separate directories in the src directory
I have 3 main features:
- Form handling
- Querying the data from the API
- Listing the Search Results
I have created a custom hook useForm
and used within it useReducer
as I [find it more proficient with forms data] (https://dev.to/ahmedsarhan/the-trap-of-the-usestate-hook-with-forms-2d86)
I have then created a context to wrap the page to access the form data through it and consumed the useForm
within it
this helps me avoid drilling props and raising the level of my useForm
hook just to check for the value of the searchType
.
I have also created couple helper hooks useDebounce
and useInfiniteScroll
which both can be used in different modules, so they are in the shared app
directory.
I have created useFetchData
hook to keep the useForm
a bit clean as it's consuming and handling a lot of computations and data so it's become a big chunk of code.
-
I created an Axios instance with the baseUrl coming from the .env.local file which wasn't pushed to this codebase -> check the [Devops and Deployment section] (https://github.com/AhmedSarhan/holo-github#devops-and-deployment)
-
All the following assumes the user have typed at least 3 characters in the
searchInput
; -
I depended on debouncing the
searchInput
value to delay hitting the API calls, once the timeout is over the data is fetched, a skeleton that displays a UI similar to the data expected, once the API finishes the data listing replaces the skeleton. -
If the user scrolls down to the bottom of the list and the amount of the data in our
redux-store
is less than the one stored in theusersCount
orreposCount
coming fromtotal_count
index that's being sent with APIs response, the App will fetch another page from the same endpoint. -
If the user changes the selected option of the
searchType
, the App will clear the data on the store and fetch again for the same query in thesearchInput
. -
On changes in the
searchInput
value, the same scenario apply.
-
I decided instead of deploying to Vercel which have their own CI/CD out of the box and deploy right away on every new commit to the
main
branch, to create my own CI/CD usingGithub Actions
and deploy toFirebase
. -
I have stored the
REACT_APP_API_URL
and theFirebaseConfig
values in the Github repo secrets which seem very secure and easily accessible by the github actions [see here] (https://github.com/AhmedSarhan/holo-github/blob/main/.github/workflows/firebase-hosting-merge.yml) -
I have created two CI/CD one for what should be the projects production connected to the
main
branch and deploys on each new commit to themain
branch with the caveat that the "No one" can commit to themain
branch directly and the only path ispull-request
. -
The
pull-request
approach mentioned above would be vey beneficial in a team setup as you can prevent merging without a PR review and approval, this means you usually have a code-review, keeping the code quality at a good level "very opinionated at my side 😁". -
The Second CI/CD runs on every new
pull-request
or a commit to source branch of thatPR
creating on completion apreview-url
that lasts for almost 2 weeks which allows teams to test on atesting-env
without deploying to production before completion.
- Usage of a data caching mechanism on the server e.g.
RTK-Query
. - Writing more inclusive Tests with mocks for the api calls.
- using the router params to include the
searchInput
andsearchType
values to the page url to allow users to copy and share the url for better ux.
This project was bootstrapped with Create React App, using the Redux and Redux Toolkit TS template.
In the project directory, you can run:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will reload if you make edits.
You will also see any lint errors in the console.
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
Note: this is a one-way operation. Once you eject
, you can’t go back!
If you aren’t satisfied with the build tool and configuration choices, you can eject
at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.