UI Review guidelines and best practices

 

UI Review guidelines and best practices

 

** One of the most important suggestion is to start building a npm package/library for all the UI pure components and reuse them across applications **

UI Flow/Process





Steps to be followed by developers

  • Recommended is to use VSCode editor for all frontend work
  • Install VSCode extentions for ESLint, Prettier, Code spell checker and
             




                                                                                                                                           
  • Good to have - ES7 React/Redux/GraphQL/React Native, Html CSS support, auto rename tag, Javascript (ES6) code snippet, Bracket Pair Colorizer, REST Client, Git Lens, CSS peek
  • Use CRA for all the projects
  • Modify global setting - Format on save to true
·       npm i -D prettier eslint-plugin-prettier eslint-config-prettier eslint-plugin-react-redux
·       npm i -D commitizen commitlint cz-conventional-changelog
·       npm i -D @commitlint/cli
·       npm i -D @commitlint/config-conventional
·       npm i -D lint-staged

OR
Make sure to have the dev dependencies from the git template project

https://github.com/ktatikonda/UI-Guidelines/

Below section for sonar integration need to be finalized once the unit testing framework is confirmed

  • Install and configure sonar-qube-scanner and jest-sonar-reporter/cypress-sonarqube-reporter

          https://www.npmjs.com/package/sonarqube-scanner

          https://www.npmjs.com/package/jest-sonar-reporter OR https://www.npmjs.com/package/cypress-sonarqube-reporter

  • Scripts need to be added for sonar

         https://www.npmjs.com/package/husky

 

Best Practices

React

Linter can enforce:

  1. Basic Rules
  2. Class vs React.createClass vs stateless
  3. Mixins
  4. Naming
  5. Declaration
  6. Alignment
  7. Quotes
  8. Spacing
  9. Props
  10. Refs
  11. Parentheses
  12. Tags
  13. Methods
  14. Ordering
  15. isMounted

Manual: 

  • Seperate presentational/functional and container components (otherwise leads to inner state and props confusion) [Can be automated to some extent using linter rule]
  • Hierarchy of components should be well designed
  • Only essentials should be written in render function, no side effects
  • Error boundaries must be used
  • Do not use PureComponent if you are building stateless functional component and need lifecycle methods, use the useEffect hook instead. The performance benefits are realized when the data passed to the component is simple. Large nested objects, and complex arrays passed to PureComponents may end up degrading the performance benefits
  • Use Resilient Components:

The principes are the following:

Redux 

Linter can enforce:

Manual: 

·       Redux is based almost completely on 3 main principles:

    1. Single Source of Truth: This is a main tree object which holds the entire state of your application. It’s meant to help your application to be seen as a whole.
    2. State is Read Only: The state of the application stops being mutable, the only way to change the state is by triggering an action, which itself will work as a log of what, when, and why changed in the state.
    3. Changes are made with pure functions: This means that only an action (or an event) can trigger a Reducer which will take the previous state and return a new one.
  • Normalizing State Shape to handle objects:

https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
https://redux.js.org/recipes/structuring-reducers/updating-normalized-data

  • Do not mutate state - Use tools such as redux-immutable-state-invariant to catch mutations during development, and Immer to avoid accidental mutations in state updates.

               https://www.npmjs.com/package/redux-immutable-state-invariant

               https://github.com/immerjs/use-immer

  • Avoid dispatching many actions sequentially
  • Use the redux DevTools extension for debugging

Hooks

Linter can enforce:

  • Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)

  • Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions. Instead, you can:

  •  Call Hooks from React function components.
  • Call Hooks from custom Hooks.

By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

Manual:

  • Use a useEffect for a single purpose - avoid using dependency array with multiple values
  • Use custom hooks whenever you can

·       Conditionally run useEffect the right way

·       function App() {
·         const [varA, setVarA] = useState(0);
·         useEffect(() => {
·                if (varA >= 5) return;
·                const timeout = setTimeout(() => setVarA(varA + 1), 1000);
·                return () => clearTimeout(timeout);
·         }, [varA]);
·         return <span> Var A: { varA } </span>;
}
  • Type out every prop inside useEffect in the dependency array

use if (varA > 0) return; condition instead of [] dependent array

 

Import Rules

Linter can enforce:

Static analysis

Helpful warnings

Module systems

  • Report potentially ambiguous parse goal (script vs. module) (unambiguous)
  • Report CommonJS require calls and module.exports or exports.*. (no-commonjs)
  • Report AMD require and define calls. (no-amd)
  • No Node.js builtin modules. (no-nodejs-modules)

Style guide

JS and ES

Linter can enforce:

  1. Types
  2. References
  3. Objects
  4. Arrays
  5. Destructuring
  6. Strings
  7. Functions
  8. Arrow Functions
  9. Classes & Constructors
  10. Modules
  11. Iterators and Generators
  12. Properties
  13. Variables
  14. Hoisting
  15. Comparison Operators & Equality
  16. Blocks
  17. Control Statements
  18. Comments
  19. Whitespace
  20. Commas
  21. Semicolons
  22. Type Casting & Coercion
  23. Naming Conventions
  24. Accessors
  25. Events
  26. jQuery
  27. ECMAScript 5 Compatibility
  28. ECMAScript 6+ (ES 2015+) Styles
  29. Standard Library

Manual:

  • Keep in mind JS hoisting while declaring variables
  • Use nullish coalescing operator instead of null and undefined checks
  • Keep your code modularized and specialized
  • Import at function level and not at library level. 
  • JS executes complex statements faster than multiple statements, so make it compact
  • Use switch case only if cases are >= 5, else if performs faster
  • Collate, minify and optimize your code in a build process.
  • Use hasOwnProperty while looping For in
  • Load Scripts at the end of body tag
  • Declare Variables Outside of the For Statement (in loop you might be accessing DOM)
  • declare the conditional statement variable also outside the loop if its not going to be dynamically changing
  • Use module pattern to avoid global variables
module = function () {
        var current = null;
        var labels = {
               'home': 'home',
               'articles': 'articles',
               'contact': 'contact'
        };
        var init = function () {};
        var show = function () {
               current = 1;
        };
        var hide = function () {
               show();
        }
        return {
               init: init,
               show: show,
               current: current
        }
}

 

  • Raw JavaScript Can Always Be Quicker Than Using a Library

          If written correctly and considering null, undefined and falsy conditions, raw javascript turns out to be faster than third party libraries like jQuery, loadash, underscore. But in certain cases these libraries perform better because of            the implementation decisions taken by the library that are browser specific. 

          So as a generic rule if native function being used is simple and more readable, then go for native function otherwise use the library.

          Examples: 

               - array.find performs better than _.find (https://measurethat.net/Benchmarks/Show/4831/0/native-find-vs-lodash-find#latest_results_block)

               - But _.each performs better than forEach or map functions (https://measurethat.net/Benchmarks/Show/4841/3/array-iteration-vs-each-vs-map)

  • Never use Eval
  • Avoid using the line comment //. /* */ is much safer to use because it doesn’t cause errors when the line break is removed.
  • Don't Use Short-Hand like removing braces
  • Use JS Lint if project is only on JS and TS Lint for typescript
  • Keep DOM Access to a Minimum
  • use native methods like join to build/concatenate a string
  • Everything that is likely to change in your code includes labels, CSS classes, IDs and presets. Put these into a configuration object and making this one public
  • Reduce Globals
  • Use Progressive Enhancement to avoid scenarios where javascript is diabled - Better SEO and accessibility
  • Avoid Heavy Nesting, use reusable functions or recurrsion instead
  • Don't Pass a String to "SetInterval" or "SetTimeOut" - because it runs in the global scope, has performance issues, is potentially insecure if you're injecting any parameters
  • Instead pass function names like - setInterval(someFunction, 3000)
  • Most of the times when the property names are small sequential integers, you should use an array. Otherwise, use an object
  • Optimize Loops - Don’t make JavaScript read the length of an array at every iteration of a for loop, Keep computation-heavy code outside of loops. This includes regular expressions but first and foremost DOM manipulation.

·       Don’t Trust Any Data

Don’t believe the HTML document
  any user can meddle with it for example in Firebug.
Don’t trust that data reaches your function is of the right format.
  Test with typeof and then do something with it.
Don’t expect elements in the DOM to be available.
  Test for them and that they indeed are what you expect them to be before altering them.
Never ever use JavaScript to protect something.
  JavaScript is as easy to crack as it is to code

  • Never create cookies with sensitive data from JS, Use the HttpOnly attribute to prevent access to cookie values via JavaScript
  • Add Functionality with JavaScript avoid doing DOM operations
  • No overrides for lint rules
  • One function should have only one action
  • Avoid using prototype and override methods
  • Always have a catch block with promises and arithmetic operations

CSS

  • Use EMs for media queries.
  • Use REMs for sizes and spacing - setting font-sizes based on the font-size of the root HTML element
  • Avoid using pixels
  • Use flexbox for designing layout in one dimension - either a row or a column. Use CSS Grid layout for designing two-dimensional layout - rows, and columns at the same time
  • Recommendation is to use CSS in JS for all new work.

Generic

  • Accessibility support
  • Localization
  • Unit Testing Framework Suggestion - React Testing Library with JEST
  • Code documentation 

 

 

 

 

 

Comments