There is no silver bullet in Software Engineering.
A high-quality React application, and indeed any good front-end application, should exhibit the following basic characteristics for success in modern web development:
- High performance and good user experience (UX = User Experience)
- Simplicity and ease of maintenance resulting in high efficiency (DX = Developer Experience)
To achieve these goals, here are some experiences and principles summarized by @Arno during his daily React development that emphasize the importance of thoughtful design, effective component decomposition, and the strategic use of React performance optimization techniques.
🚀 Core React Design Principles for Scalable Applications
-
Emphasize Design First, Then Implement as a foundational React best practice. Start with macro design before implementation for robust React application architecture.
- Carefully choose foundational libraries and engineering architecture based on business requirements. Remember to evaluate the long-term viability and community support of these libraries to ensure they align with your project's goals and can adapt to future changes.
Less is more
because it helps to reduce complexity, making the codebase easier to maintain and understand while minimizing potential issues that may arise from over-reliance on numerous dependencies. This is a core tenet of modern React development. - Consider using the right React state management patterns, using appropriate frameworks and libraries to decompose application states into maintainable parts, which is crucial for scalable React applications.
- Consider the decomposition of UI components, a key aspect of effective React component design, for example:
- View components (components with application-level states), usually tied with
router
level and are responsible for rendering the overall layout and structure of the application, ensuring a seamless user experience as users navigate through different routes. - Business components (foundational components with business characteristics, such as an internal contact selection component), encapsulate specific business logic and functionality, allowing for greater reusability and maintainability across different parts of the application.
- Basic components (general-purpose components without business characteristics, such as
AntD
and Material-UI, which provide reusable UI elements that can be easily integrated into various applications without being tied to specific business logic.
- View components (components with application-level states), usually tied with
- Focus on the design of data
immutability
in React, which can have remarkable effects in specific scenarios especially when it comes to optimizing React app performance by preventing unintended side effects and ensuring predictable state management throughout the application lifecycle. - Especially consider the "orthogonality" between React components, meaning the separation of responsibilities, extracting them into different view components for easier system maintenance and independence. This is a key principle in React architecture for large applications and also facilitates testing.
- Business UI Scenario: rendering business UI elements
- Data Fetching Scenario: data fetching and UI state presentation
- Global State Scenario: global state listening and management
- Data persistence Scenario: components responsible for data I/O persistence, etc.
- ...
- Carefully choose foundational libraries and engineering architecture based on business requirements. Remember to evaluate the long-term viability and community support of these libraries to ensure they align with your project's goals and can adapt to future changes.
-
Pay appropriate attention to the "Evolutionary Design" of the current front-end application, considering the impact of external business and technological changes on the current architecture and future response strategies.
-
Configure unit testing for foundational business libraries, especially for foundational units that need to be used across different businesses, ensuring quality, and establish a DevOps testing mechanism, including unit tests, integration tests, or E2E tests. Use AI to automate the testing process, allowing for quicker feedback loops and enhanced accuracy in identifying potential issues before they reach production. → AI Driven FrontEnd Development
-
Develop scalable systems and adhere to design methods such as
IoC
(Inversion of Control) andDI
(Dependency Injection), and even introduce microkernel architecture; specific references can be consulted.- Use the proper module-loader or module-isolation mechanism to ensure that components remain decoupled and can be independently developed, tested, and deployed, which enhances the overall maintainability and scalability of the application. → 📦 Ways of Organize Web Modules in Browser
-
When facing complex business logic in systems, consistently use the object-oriented (OO) programming paradigm for structural design and functional implementation, enforcing the use of TypeScript as the programming language.
⚡ React Performance Optimization Techniques
- Follow these React performance optimization techniques, based on the official design specifications of the React framework, to build fast React applications. To avoid issues with duplicate rendering, refer to the React Re-renders Guide, and extract some important principles for optimal React performance:
- Understand the triggers for re-rendering: parent node re-rendering, node state changes, Context value changes, and Hook changes.
- 🚫 Do not create component classes in the render function (this will re-render the entire child component tree every time).
- ✅ Try to pass state down to "leaf" nodes as much as possible, minimizing intermediate states or related states.
- ✅ Pass children as props.
- ✅ Pass Component classes as props.
- ✅ Use React.memo to prevent updates from propagating downward.
- ✅ Correctly use useMemo() and useCallback() to cache computationally expensive values and functions. This is a cornerstone of useMemo useCallback best practices.
- ✅ Use keys as unique identifiers (for lists).
- ✅ Use the useMemo mechanism for Context values.
- ✅ When injecting Context, separate data and API methods.
- ✅ Differentiate Context functions; avoid a single Context.Provider to rule them all.
- ✅ Use Context Selectors to update views as needed.
- Understand the triggers for re-rendering: parent node re-rendering, node state changes, Context value changes, and Hook changes.
- Decompose React view components (FunctionComponent) as much as possible; avoid combining them into a large function, as using
useState()
within a function will cause the entire node and subtree Render function to execute, affecting React performance. This is a key aspect of modern React development guidelines. - Consider caching strategies at every level and keep them in mind. Additionally, use libraries that support caching appropriately to implement reasonable caching in high-frequency scenarios.
- Always enable the RenderHighlight feature in React DevTools to visually detect the actual rendering situation of the DOM. If duplicate rendering or imprecise rendering areas (redundant rendering) are found, immediately perform targeted optimizations.
- Pay attention to package size; use mechanisms like TreeShaking to eliminate redundant dependencies and be cautious when introducing external dependencies.
🛡️ Ensuring Stability in React Applications
- For stable React applications, use mature, high-quality open-source libraries, but be sure to use version locks (e.g., via
yarn.lock
orpackage-lock.json
) to avoid potential breaking changes that could disrupt the stability of your application during updates. This is a fundamental React best practice. - When using Hooks, pay special attention to prevent circular dependencies; handle Hooks dependencies with great care. Using the linter can help identify potential issues early in the development process, ensuring a more stable and reliable application. This is crucial for modern React development.
- Be mindful of
XSS
protection; non-trusted content needs to be escaped before output.
🛠️ Writing Maintainable React Code: Best Practices
- Unify the
code writing style
and React coding best practices within the team. Reading resources likeClean Code
helps ensure all team members adhere to best practices for maintainable React code. Use linters (e.g., ESLint with React plugins) to automatically enforce coding standards and catch potential errors early in the development process, fostering a collaborative environment where quality code is prioritized. - Unify the code organization style within the team: directory organization, project organization (MonoRepo), etc. For large projects, it is reasonable to decompose and allow technical and business autonomy; oligarchic applications are not conducive to large team operations. Good React project structure is key.
- Reasonably use design patterns and design principles within the team to improve program structure and behavior organization, facilitating effective communication to form a consensus and unify the design language.
- Strictly adhere to the basic principle of separating views and logic (this is easy to say but often not followed).
- Use layered design thinking to manage application state reasonably, following established React state management best practices.
- Control file length; often, the root of complexity comes from not segmenting and separating code in a lazy manner, leading to lengthy files. 500 lines may be the limit.
- For extracting reusable React components, refer to discussions on frontend component design principles (like those found at Frontend Mastery on Architecture) which provide feasible guidelines for modern React development.
⚙️ React Engineering Principles for Efficient Development
-
Use AI Code Copilot to streamline the React development process, enhance developer productivity, and reduce the likelihood of errors by providing intelligent code suggestions and automating repetitive tasks. This is a growing trend in modern React development.
-
Utilize good coding tools to enhance programming efficiency, such as
VSCode
and its powerful plugin ecosystem. -
Choose mature code build tools; for server-side rendering, use Next.js; for client-side SPAs, choose Vite; for static
SSG
, consider Astro, etc. Additionally, consider usingTurborepo
for MonoRepo project management. These choices impact your React development workflow. -
Maintain unified React Boilerplates within the team and provide a progressive upgrade mechanism that incorporates these React best practices and project guidelines.
-
For complex web systems that require frequent maintenance by multiple teams, consider introducing architecture to enhance engineering collaboration efficiency and code isolation while maintaining SPA-level experiences. This is vital for scalable React applications.
-
Use new engineering technologies moderately without affecting stability, such as
vite
,esbuild
, orswc
to improve development efficiency. -
Focus on the efficiency of interface coordination, using GraphQL or some interface generation and mocking tools, as well as automatically generating
*.d.ts
data model definition files. Use OpenAPI Spec to Typescript API Client to ensure seamless integration between front-end and back-end services, facilitating smoother data exchanges and reducing the potential for errors in type definitions.
🚀 FullStack React Development with a Server Backend
For a comprehensive full-stack React development guide, including how to effectively integrate server-side rendering with API routes for seamless data fetching and optimal performance, refer to Arno's 🪐 Next.js Full Stack App Architecture Guide. This guide details building scalable React applications with a server backend.