Invoice App - ReactJS, Styled-Components, Framer Motion, Webpack

Solution retrospective
Hello👋!
This is an invoicing application build with ReactJS and styled-components. The application is used to manage invoices and allows the user to create, read, update, filter by status and delete invoices. There is an option in the app to switch between a dark and a light theme. All transitions are smoothly displayed by using Framer Motion library to create animations. It was by far the largest and most comprehensive project I have done so far. It showed me how important it is to plan so that you don't have to change things that previously worked well in the middle of the project. A valuable lesson!
- The first time I used
useReducer
hook to manage application state. I noticed that my state logic getting more complex as the few elements of my state relies on the value of another element of my state. Read More - Together with useReducer, the
useContext
hook turned out to be handy. It allowed me to create common data that can be accessed throughout the component hierarchy without passing the props down manually to each level which in turn allowed me to avoid Prop drilling (also called "threading") that is the process you have to go through to get data to parts of the React Component tree. Read More(1). Read More(2) - In order to create a theme switcher and provide colors for components I used styled-components
<ThemeProvider>
wrapper component. By leveraging the theme provider I only need to write my styles in one single object and invoke them in any component that is a descendant of that provider. Read More - When creating the form I learned what Controlled Component is. In HTML, form elements such as
<input>
,<textarea>
, and<select>
typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated withsetState()
. Then the React component that renders a form also controls what happens in that form on subsequent user input. Read More - To make application more
ADA compliant
(which means the website should be entirely accessible using just keyboard) I prevent focus go outside the modal once the modal is opened. In this case, the focus trap turns on when the form or modal with invoice deletion/status change is opened. In order to create an accessible modal I followed this great tutorial that follow the WAI-ARIA Practices. - To animate the pages transitions and modals I used Framer Motion API. Framer Motion is an open source, production-ready library that's designed for creating creative animations. In order to support users who have enabled their device’s Reduced Motion setting and make accessible animations I used
useReducedMotion
hook. Based on whetheruseReducedMotion
returnstrue
or not we're passing different values toanimate
. That replace position transitions with opacity. Read More(1). Read More(2) - Handle 404 routes in React Router and provide a fallback component for displaying an imfamous
404 Page Not Found
error to the user. Try to enter a page that doesn't exist - like 'invoice-tediko.netlify.app/gotcha'
You can find more about the things I used in the project in the README on github. I just wanted to point out most important things here.
Questions:
- As I am changing the theme and many colors are changing, I wasn't sure how to make the color system. I end up with object for dark/light theme with colors of each element and a few colors that are common. How would you approach that?
- I didn't want to create a special component for my headings since they have no logic. I created helper utility called typographyStyles where I put all styles that I use across application. Not sure about that tho.
- I am curious what method you use to name your components. I personally stick to option #2 because it has the most pros for me and a little longer import doesn't bother me.
Bugs:
- Clicking twice quickly on header logo (navigate to home page, which render Invoices component) causes component not to render. I am convinced that this is related to Framer Motion and
AnimatePresence
that allows components to animate out when they're removed from the React tree. I think build in isPresent state when clicking twice quickly didn't change the state. I overcame this problem with simpleevent.preventDefault()
with asetTimeout
function but..
Additional feedback or a criticism will be appreciated Thanks! 😁!
Please log in to post a comment
Log in with GitHubCommunity feedback
- @alex-kim-dev
Awesome job, Tediko! I see you put a lot of effort into this project. I like that everything is well organized, files, code, commits, it's really easy to navigate through and find out how things are connected to each other. And great job on writing readme! Here is some feedback:
- animation feels slow, I would adjust duration to be around 200-300 ms
- accessing local storage might throw errors in some cases, for example if a user turned off all cookies in Chrome, some older browsers might throw even if only third-party cookies are blocked. So It's a good practice to wrap it in a try-catch block
- there are a few places with a lot of nested if-else statements, like in
formValidation.js
. That's increasing cyclomatic complexity and makes code hard to understand, especially for people not familiar with the codebase. Try to avoid nesting conditions. Ideally it should look like this:
() => { if (conditionA) return value1; // the following code will be executed only if(!conditionA) if (conditionB) return value2; // the following code will be executed only if(!conditionA && !conditionB) return value4; }
- would be nice to work a bit on loading performance. I had DOMContentLoaded: 1.99s on fast connection and 5.32s on simulated "fast 3G".
Good luck and until the next awesome project!
Marked as helpful - @mattstuddert
Another awesome solution, tediko. Nice work!! 👍
It's so clear you pay close attention to the details, and your write-ups are extremely helpful for others to read through. To answer your questions:
- Using an object inside your
theme.js
file is fine and is typically the way this would be done using Styled Components. You can still use CSS variables if you prefer, though. One thing I would say is that you could use broader colour names. At the moment, you're duplicating a lot of the colour values. We use names likeprimaryBlue
,accentGreen
, etc., in our code. - Different people will have different approaches to this. We have a single
Heading/index.js
file that handles all possible heading styles. I'll send you the code for that file in Slack so you can take a look. It might give you some ideas about alternative approaches. - As you can see from above, we use the
index.js
pattern. We'd also have the Storybook stories and tests in the main component folder, e.g./Heading
. We like that usingindex.js
saves a few characters here and there 😅
I hope this helps! Keep up the awesome work! 🚀
Marked as helpful - Using an object inside your
- @arkharman12
Great work!!! I was kinda surprised by the amount of commits in the repo. Do you like to commit every change you make?
Join our Discord community
Join thousands of Frontend Mentor community members taking the challenges, sharing resources, helping each other, and chatting about all things front-end!
Join our Discord