React is one of the most popular JavaScript libraries for building user interfaces, and one of the reasons it gained so much popularity is its Component-Based Architecture. React encourages building UI in reusable components, allowing developers to build complex user interfaces more efficiently.
Since we would be dealing with components in react, it is essential to follow best practices for component design. In this article, we'll explore 10 best practices that will help you write cleaner, more maintainable, and reusable React components:
1 - consistent formatting: There are several ways to create a functional component in a react application, some of them include - arrow functions, functions declarations and function expression. All of these are valid ways to create a component but it is important to stick with 1 way of declaring a component in your application. Inconsistent component functions can make your code difficult to read.
2 - Follow a design pattern and stick to it: Following an already established design pattern in your react application keeps your components organised, easy to read, test and make changes. Two of already established practices include:
a. Container/Presentational Pattern: Following this approach ensures a strict separation of concern between the business logic and user interface. Where the container components manages data and state, while presentational components focus on rendering the UI.easier to test.
b. Flux Pattern: Flux pattern was introduced by the facebook team to introduce a unidirectional data flow in your react application, this is commonly enforced using state management libraries like redux.
3 - Single Responsibility Principle: Each component in your app should have a single responsibility, focusing on one specific functionality. Following this principle makes the component more reusable and less prone to bugs. For example, in most cases, a "Button" component should handle ONLY rendering and user interactions.
4 - Prop Types and Default Props: Defining prop types and default values ensures component reliability. PropTypes validate the expected types of props, catching potential bugs early. Default props provide fallback values if a prop is not explicitly passed, avoiding unexpected behaviour.
Do not use typescript in your project? This is totally fine, you can still achieve this using the propTypes library.
npm install --save prop-types
import PropTypes from 'prop-types';
Button.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
};
function Button(props) {
const {name, age} = props
return (
<div className='App'>
<h1>Hello {name}</h1>
<h2>I am {age}</h2>
</div>
);
}
export default Button;
5 - Destructuring Props: Take advantage of the object destructing to access props. This can reduce verbosity in the code and enhance readability. It also improves the clarity of component interfaces and makes it easier to identify which props are used.
An example of this can be seen in the Button component code shared above: const {name, age} = props
6 - Prop and state: It is crucial to understand the difference between your props and your state. Props are static data passed around within components. They are immutable and do not change. State is used to manage dynamic data within a component. It represents the internal state of a component, allowing it to handle and respond to user interactions, events, or changes in the component's own logic.
7 - Styling: Styling is an important aspect of React component design. The most important thing here is to pick a styling of choice and remain consistent with it across components in the library. Some of the most popular styling choices include:
- Use CSS-in-JS libraries: CSS-in-JS libraries like styled-components can make it easier to style your components and create reusable styles.
- Use CSS modules: CSS modules allow you to create component-level styles that don’t clash with styles from other components.
- Use utility-first CSS framework like tailwind css
- Use UI libraries such as material-ui, ant design, chakra etc.
- Create reusable styles: When creating custom styles with css, sass etc. It is important to prioritise reusable styles that can be used across your application. This can help you create a consistent visual style and make your components more maintainable.
8 - Testing: Testing is perhaps one of the most severely underrated aspects when it comes to building react components. Here are some best practices to consider:
- Use libraries like Jest and cypress to write unit tests for your components.
- Test props and state: Test that your components handle props and state correctly. This can help you catch bugs that might not be apparent in the UI.
- Test end to end user interactions: Test that your components handle user interactions correctly. This can help you ensure that your components are user-friendly and responsive.
Conclusion:
By following these best practices for React component design, you'll be able to create cleaner, more maintainable, and reusable components. Each practice reinforces important principles such as single responsibility, reusability, prop validation, and performance optimisation. Incorporating these practices into your React projects will contribute to better code quality, improved developer experience, and ultimately, more robust applications.
Remember, mastering these best practices requires practice and continuous learning. Stay up-to-date with the evolving React ecosystem and always strive for code that is efficient, readable, and easy to maintain.
Happy coding ☕️
Top comments (18)
Adding to the article:
Item 4:
prop-types
is not longer needed. You can simply use TypeScript or JSDocs instead:Item 5: You can deconstruct properties in the header of the function, no need to create an extra const declaration:
Item 6: State/props aren't immutable, but indeed should be treated as such. So you can mutate them, but you shouldn't. One wat to help with this is to use the
readonly
keyword in TypeScript, so the editor tells you when you're mutating something you shouldn't.Cheers!
hi @lukeshiru it's good to go with typescript, but inn our team just few people only know typescript!
So as for now we continue the
prop-types
but if i component withPropTypes.oneOf
this is not auto suggest. so i feel it's useless ☹️This is my code! can u give me suggestion or idea why
oneOf
not auto suggest when i import & use this component?As I mentioned in my original comment you can use TypeScript or JSDocs. JSDocs will add some "type safety" and auto-completion without going full TypeScript (you can read more about it here).
So the code of your component using JSDoc (plus other improvements such as using
to
instead oflink
, setting the content once, removing shortcircuits and improving overall setup of theclassName
) could look something like this:You can see it working and try the autocompletion and type checking in this CodeSandbox.
Cheers!
wow! really thanks @lukeshiru 😊
@lukeshiru thank you for introducing JSdocs! Great perspective.
Typescripts is the default type safety for most websites building for prop-types is good to know for those who are yet to adopt TS
Typescript is definitely worth learning. Your teammates that currently don't know Typescript will start writing much better code if they learn it! Also you can combine Typescript with runtime prop types checks if you're using babel github.com/milesj/babel-plugin-typ...
it's looks interesting!
The reason you shouldn't mutate props is that props are immutable in the user interface.
That's right @lukeshiru and Typescript makes things easy and smart.
For item #1, I typically use arrow functions, but that doesn't allow you to have a one liner for a default export. i.e. this invalid syntax:
But this works:
and this works:
I prefer the one liner so I typically choose
function
approach for default exports. You can still do named exports withconst
though:That's not quite right, you can export default an arrow function like this:
Tho I generally advocate to not use default exports at all, and just export everything with a name (makes refactoring way easier).
You can have a nameless default export like that, but then your editor doesn't know how to import it by name when using autocomplete to resolve something that's not imported yet.
That makes sense, though I like the clean look on the import side in the case of default exports. When I read the top of a file, I generally think that named exports in an import statement are "extras" of what that file is exporting, whereas the default is the "main" thing to import. It's also slightly less things for our eyes to parse when scanning imports, when the curly braces are absent. Definitely all preference though. I think pragmatically have them all named makes sense, as you suggested.
This is very much an IDE issue, not a code issue. There's no technical reason not to autocomplete the folder or file name for the import (and, I could be wrong, but I thought jetbrains IDEs did exactly that?).
That said, I'm personally not a fan of immediate default exports anyway. Having a named const that later gets default-exported allows, for instance, attaching further properties to the exported object without needing to rearrange code.
Totally agree. Nice points here, thank you 👍
i enjoyed every bit of the article and even more the discussions in the comments.
thank you all guys, it’s helping my newbie career.
Great overview article. I really liked you mentioned container components, might have been good to expand on that and mention custom hooks to handle logic and state.
Great suggestion, I should expand on this
Some great tips in there.
I'd argue point 1 is negligible though. I don't think mixing between how you define your components makes it harder to read. Messy maybe, but hard to read, no.
As @mickmister alluded to, different ways of defining them affect how you export and ultimately import your component so maybe allowing both patterns is advantageous in different scenarios?