November 2nd 2021 would make it officially a year since I got into frontend engineering. Over these long months, I’ve tried my hand at multiple small-medium sized projects, learned a number of core frameworks and established myself with freelance clients.
The long road of trial and error was made much shorter by my mentor and a glorious server of Senior devs who have always been there for me. Today I aim to take a look at my carbs…sorry, spaghetti code and do some refactoring with the new stuff I’ve learned over time.
If you’re anything like me, you cringe and shudder at your older code…Wait WTF was that? I wrote that shit? sheesh! But in the end, you understand and appreciate going through that phase and falling in love with the learning process.
Structure
My approach to breaking down everything over this series will be.
What I needed to to -> What I did -> What I should have done/What I could do differently… This cycle is in no way rigid and as I still regard myself as an eternal junior, I welcome any other input I can get regarding improving on my code!
The App — Fluxara
https://github.com/Geller99/Fluxara
Feature List:
- Fetch and display news using news API
- Implement Routing
- Implement error handling
- Capture user data and use event handlers
- Search and Load Features with API calls
- Geolocation and Weather display using APIs
- Familiarize myself with Hooks and application Lifecycle
- Responsive Design CSS
The first thing I did back then was start with building out the CSS for each page…the idea was, if I had a fully responsive and working design, it’d be much easier to just “Add” functionality…right? Now? I find it’s much easier FOR ME to build out components and add functionalities before actually styling them completely. I just feel its much easier to realize a component isn’t needed or could be moved elsewhere while messing with logic… rather than needing to switch up my CSS later-on after painful realizations.
What I needed #1: Folder Structure
What I did…. LOL
What I should have done?
A few folder name changes..
- Models replaces “context”: represents state storage and holds modules for handling state actions (read Clean Architecture)
- utils replaces “helpers”: holds globally used items like images, fonts, svgs, icons etc
- services replaces “features”: this holds external information and business logic. API calls and queries happen in here, class definitions and instances for any additional non-react logic is contained here.
I’d also love to point out that there is no need for a main.css and that index.css should exclusively contain imported files from the styles folder. Keeping the surface devoid of the details.
What I needed #2: App js Logic Structure
A key part of programming is understanding WHY your code behaves the way it does vs what you ACTUALLY want it to do. It’s actually one of the principles behind testing. Write failing tests for features, then implement said features.
Let’s breakdown what App.js should be doing vs what TF I had it doing
Yup, hell no… too much “stuff”, I shouldn’t ever need this many lines in my app js file. I could move trigger API to services? refactor it to be less rigid and reliant on external variables and make the locationHandler() call elsewhere…
What’s happening in this code? Why does it need to happen in app.js? can I move it elsewhere and still efficiently implement my feature?
Everything before the useEffects call is meant to be global state, creating values at the top level component, then passing them along with setter methods to children components. This can very easily be implemented using contextAPI and passing those variables and setters into a custom useStore Hook.
The useEffects() call there was meant to trigger the geolocation API and provide that data for the dashboard component. Again, absolutely not needed in app.js
The triggerApi() was a function dependent on too many variables, meaning it wasn’t pure and broke my now refined functional approach to coding - extract to services and refactor.
In my sexy opinion, app js should be devoid of logic or implementation details, it should hold top-level routing and in most cases not concern itself with lifecycle methods or local state.
In more recent projects, this is as simple as it has ever needed to be… hold state providers, contain router component and that’s it! Maybe a Boolean loading flag? maybe?
conclusion? abstract away the implementation details…
What I needed #3: services
The main goal of this app is fetching and displaying data. Let’s examine my implementation of the axios fetch methods and see if we need to refactor.
Earlier, we changed a few folder names to represent what purpose certain folders served. The services folder now holds modules that deals with external services like databases, apis, sockets etc…
first call: Geolocation API
Right away, my naming convention is wrong… a much more suitable name would be FetchUserLocation()
Next up, we should implement a bit more safety inside the .then(), perhaps an if-statement checking to ensure the data is available? and has a .data property? What if the structure of the api changes and data.data.city/country isn’t in that order anymore? what values are provided when the api fails? undefined? null?
We need to test the negative case! also, async/await ?
The original goal with this method was to construct it so setter methods of local state variables can be passed in as arguments. An approach don’t necessarily favor currently. I believe a more agnostic version of this function would take in url and data parameters which can be provided from any part of the app for any specific api endpoint.
At this point, the question of architecture comes in. Do we make this into a useAxios hook? is it much easier to implement and much more reusable? Yes…possibly, but!
That would be coupling business logic with React…which means tying in multiple layers of our application we need to keep isolated from each other. So, I’ll opt against useAxios here.
I’ll take a break here for the first stage of this refactor.
To review, we covered:
- folder structures
- App.js structure and logic
- Services and Apis
In the next phase, we will examine more API calls and implementations and breakdown some more architectural choices I made back then vs what I’d do instead today…atomic design being the major decision change here.