Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found

Submitted

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

P
tedikoβ€’ 6,580

@tediko

Desktop design screenshot for the Invoice app coding challenge

This is a solution for...

  • HTML
  • CSS
  • JS
5guru
View challenge

Design comparison


SolutionDesign

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 with setState(). 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 whether useReducedMotion returns true or not we're passing different values to animate. 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:

  1. 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?
  2. 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.
  3. 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:

  1. 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 simple event.preventDefault() with a setTimeout function but..

Additional feedback or a criticism will be appreciated Thanks! 😁!

Community feedback

Alex Kimβ€’ 1,325

@alex-kim-dev

Posted

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

3

P
tedikoβ€’ 6,580

@tediko

Posted

@Alex-K1m Thanks! Indeed, the animations are too long and therefore use of application isn't as smooth as it should be. I like web animations and I found myself using them far too much πŸ˜….

Oh, I didn't know that! Now I've take a quick look and in fact using localStorage directly can be a bad idea. I will definitely take a proper look on the topic.

Aw Jeez! Believe me, I was about to say at the top of the file that I am not responsible for the consequences of reading this validation code πŸ˜„ Validation itself was easy but pushing all error messages to object was harder part. It iterates through an object that has a string, array, or object property and within arrays and objects there are many more to check. I'm not happy with this code and know that it shouldn't look like this.

I never got into the subject of performance, but definitely I have to.

Thanks for great feedback Alex! Good luck too!

1
P
Matt Studdertβ€’ 13,611

@mattstuddert

Posted

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 like primaryBlue, 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 using index.js saves a few characters here and there πŸ˜…

I hope this helps! Keep up the awesome work! πŸš€

Marked as helpful

2

P
tedikoβ€’ 6,580

@tediko

Posted

@mattstuddert Thank you for such kind words, Matt! πŸ™‚ I'll have to work on the colors in my next projects. I think I'll stick to using object in theme.js but I'll try to use broader color names as you said. If an application had only one theme, it would be much easier. Indeed I did duplicate the colors, that is because several elements use the same color in light theme, but on the other theme they have completely different colors and I couldn't come up with a different solution.

I look through what you sent me on Slack and it does look way better than my approach. Since our headings usually doesn't have many styling except some base font styling it would be much better to have this as a reusable component and optionally upload style by passing the props. It will reduce boilerplate code for me. 🀩

Good to know! I'll stick to the component name for now. This is just a personal preference and it is very easy to switch to another method so.. πŸ˜‰ But! I think I'll start to using index.js for my utilities where I keep many different functions. So that I could import multiple utilities in one go like this:

import {
  dateToString,
  generateUID,
} from '../../../../utils';

instead of:

import dateToString from '../../../../utils/dateToString';
import generateUID from '../../../../utils/generateUID';

Anyway! Thanks for valuable feedback, as always! πŸ™‚

2
P
Matt Studdertβ€’ 13,611

@mattstuddert

Posted

@tediko, that's a good point about your colour naming and a good reason for why you went the route you have πŸ‘

I'm happy to hear my feedback helped! πŸ™‚

1
Harmanjot Singhβ€’ 985

@arkharman12

Posted

Great work!!! I was kinda surprised by the amount of commits in the repo. Do you like to commit every change you make?

1

P
tedikoβ€’ 6,580

@tediko

Posted

@arkharman12 Thanks! Commit often, perfect later, publish once. I've read that this is a good practice. Usually, when I create a component or some functionality I commit only when the job is done and from that point every change in that component / function is a separate commit. For sure there were some unnecessary commits, e.g. when I needed to check something on netlify and I needed a push for it and it creates some unnecessary commits.

Committing frequently gives the advantage of troubleshooting commits that have broken something in the build process and being able to revert back without losing too much work. Seeing changes is easier without going through a file with 1000 line additions.

4
Harmanjot Singhβ€’ 985

@arkharman12

Posted

@tediko Good to hear your perspective for doing that :)

0

Please log in to post a comment

Log in with GitHub
Discord logo

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