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/
- add .
prettierrc
config file from the git template project -https://github.com/ktatikonda/UI-Guidelines - add .
eslintrc.json
config file from the git template project - https://github.com/ktatikonda/UI-Guidelines - add .
eslintignore
and .prettierignore
from the git template project - https://github.com/ktatikonda/UI-Guidelines - Create file .
commitlintrc.json
from the git template project - https://github.com/ktatikonda/UI-Guidelines - npm i husky -D
- Restart VSCode editor if rules doesn't apply
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
- Basic Rules
- Class
vs
React.createClass
vs stateless - Mixins
- Naming
- Declaration
- Alignment
- Quotes
- Spacing
- Props
- Refs
- Parentheses
- Tags
- Methods
- Ordering
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
- react-redux/connect-prefer-minimum-two-arguments Enforces
that connect function has at least 2 arguments.
- react-redux/connect-prefer-named-arguments Enforces
that all connect arguments have recommended names.
- react-redux/mapDispatchToProps-returns-object Enforces
that mapDispatchToProps returns an object.
- react-redux/mapDispatchToProps-prefer-shorthand Enforces
that all mapDispatchToProps use a shorthand method to wrap actions in
dispatch calls whenever possible.
- react-redux/mapDispatchToProps-prefer-parameters-names Enforces
that all mapDispatchToProps parameters have specific names.
- react-redux/mapStateToProps-no-store Prohibits
binding a whole store object to a component.
- react-redux/mapStateToProps-prefer-hoisted Flags
generation of copies of same-by-value but different-by-reference props.
- react-redux/mapStateToProps-prefer-parameters-names Enforces
that all mapStateToProps parameters have specific names.
- react-redux/mapStateToProps-prefer-selectors Enforces
that all mapStateToProps properties use selector functions.
- react-redux/useSelector-prefer-selectors Enforces
that all useSelector properties use selector functions.
- react-redux/no-unused-prop-types Extension
of a react's no-unused-prop-types rule filtering out false positive used
in redux context.
- react-redux/prefer-separate-component-file Enforces
that all connected components are defined in a separate file.
Manual:
· Redux
is based almost completely on 3 main principles:
- 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.
- 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.
- 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
- 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
Static analysis
- Ensure imports point to a file/module that can be
resolved. (
no-unresolved
) - Ensure named imports correspond to a named export in
the remote file. (
named
) - Ensure a default export is present, given a default
import. (
default
) - Ensure imported namespaces contain dereferenced
properties as they are dereferenced. (
namespace
) - Restrict which files can be imported in a given folder
(
no-restricted-paths
) - Forbid import of modules using absolute paths (
no-absolute-path
) - Forbid
require()
calls with expressions (no-dynamic-require
) - Prevent importing the submodules of other modules (
no-internal-modules
) - Forbid webpack loader syntax in imports (
no-webpack-loader-syntax
) - Forbid a module from importing itself (
no-self-import
) - Forbid a module from importing a module with a
dependency path back to itself (
no-cycle
) - Prevent unnecessary path segments in import and require
statements (
no-useless-path-segments
) - Forbid importing modules from parent directories (
no-relative-parent-imports
)
Helpful warnings
- Report any invalid exports, i.e. re-export of the same
name (
export
) - Report use of exported name as identifier of default
export (
no-named-as-default
) - Report use of exported name as property of default
export (
no-named-as-default-member
) - Report imported names marked with
@deprecated
documentation tag (no-deprecated
) - Forbid the use of extraneous packages (
no-extraneous-dependencies
) - Forbid the use of mutable exports with
var
orlet
. (no-mutable-exports
) - Report modules without exports, or exports without
matching import in another module (
no-unused-modules
)
Module systems
- Report potentially ambiguous parse goal (
script
vs.module
) (unambiguous
) - Report CommonJS
require
calls andmodule.exports
orexports.*
. (no-commonjs
) - Report AMD
require
anddefine
calls. (no-amd
) - No Node.js builtin modules. (
no-nodejs-modules
)
Style guide
- Ensure all imports appear before other statements (
first
) - Ensure all exports appear after other statements (
exports-last
) - Report repeated import of the same module in multiple
places (
no-duplicates
) - Forbid namespace (a.k.a. "wildcard"
*
) imports (no-namespace
) - Ensure consistent use of file extension within the
import path (
extensions
) - Enforce a convention in module import order (
order
) - Enforce a newline after import statements (
newline-after-import
) - Prefer a default export if module exports a single name
(
prefer-default-export
) - Limit the maximum number of dependencies a module can
have (
max-dependencies
) - Forbid unassigned imports (
no-unassigned-import
) - Forbid named default exports (
no-named-default
) - Forbid default exports (
no-default-export
) - Forbid named exports (
no-named-export
) - Forbid anonymous values as default exports (
no-anonymous-default-export
) - Prefer named exports to be grouped together in a single
export declaration (
group-exports
) - Enforce a leading comment with the webpackChunkName for
dynamic imports (
dynamic-import-chunkname
)
JS and ES
- Types
- References
- Objects
- Arrays
- Destructuring
- Strings
- Functions
- Arrow
Functions
- Classes
& Constructors
- Modules
- Iterators
and Generators
- Properties
- Variables
- Hoisting
- Comparison
Operators & Equality
- Blocks
- Control
Statements
- Comments
- Whitespace
- Commas
- Semicolons
- Type
Casting & Coercion
- Naming
Conventions
- Accessors
- Events
- jQuery
- ECMAScript
5 Compatibility
- ECMAScript
6+ (ES 2015+) Styles
- 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
Post a Comment