Components
In Circle projects, we employ various types of React components to achieve scalable and reusable code, enhancing our utilization of tools like Storybook and unit tests. This approach not only streamlines development but also ensures consistency and reliability across our applications.
Component Types
In this table, we break down the differences between each type of component.
| Presentational | Containers | Pages | |
|---|---|---|---|
| Purpose | How things look (markup, styles) | How things work (data fetching, state updates) | Layout, connecting the pieces (containers and presentational components) |
| Aware of Apollo/GraphQL | No | Yes | No |
| To read data | Read data from props | Connected with Apollo Query | Read data from query params |
| To Change data | Invoke callbacks from props | Connected with Apollo Mutation | None |
| Localization | From props | useTranslation hook | useTranslation hook |
| Have Unit Test | Yes | Yes | Optional |
| Story in Storybook | Yes | Yes | No |
| Routing | From props and rendered links only | useRouter hook | useRouter hook |
Benefits of This Approach
- Improved Separation of Concerns: By categorizing components based on responsibility, we maintain a clear architecture that enhances maintainability.
- Better Reusability: Presentational components can be reused across different containers, reducing duplication and improving modularity.
- Enhanced UI Consistency: Presentational components act as a visual “palette” that can be adjusted without affecting application logic.
- Encourages Layout Components: Extracting layout components like Sidebar, Page, and Header prevents duplication and ensures a structured UI.
Presentational Components
Presentational components are focused on how things look. They are responsible for rendering UI elements based on the props they receive and do not manage state or side effects.
Container Components
Container components handle how things work. They manage state, fetch data, and interact with GraphQL/Apollo, providing necessary data to presentational components.
Pages Components
Page components serve as the structural foundation of the application, connecting various containers and presentational components to form complete views.
Naming Conventions and Syntax
Circle projects follow a consistent naming convention, favoring arrow functions assigned to constants over traditional function declarations.
Preferred
We prefer the typed version of a component (React.FC) because:
- Explicitly Defines Props: It enforces type safety by clearly specifying what props a component expects, reducing runtime errors.
- Improves Readability and Maintainability: Developers can quickly understand a component’s expected props and structure without needing additional documentation.
- Enhances Autocompletion and Developer Experience: TypeScript support provides better editor autocompletion, making it easier to work with components.
Example:
const MyComponent: React.FC = () => {}
interface MyOtherComponentProps {
/**
* Child Nodes.
*/
children?: React.ReactNode
}
const MyOtherComponent: React.FC<MyOtherComponentProps> = ({ children }) => {}Avoid
We avoid untyped function components because:
- Consistency with Modern JavaScript Practices: Arrow functions are a feature of ES6 and have become a standard in modern JavaScript development. Using them for component definitions aligns our codebase with contemporary coding standards, promoting consistency and readability.
- Avoidance of Hoisting: Function declarations are hoisted, meaning they are moved to the top of their scope before code execution. This can lead to unexpected behaviors if a component is used before it’s defined. Arrow functions assigned to constants are not hoisted, ensuring components are defined before they are used, which enhances code predictability.
- Lexical Binding of this: Although functional components typically do not use this, maintaining a consistent use of arrow functions ensures uniformity across our codebase, especially when dealing with nested functions or callbacks that do rely on lexical scoping.
Example:
function MyComponent() {}