# ๐ŸŒ„ Utopian React App This doctrine is a collection of experienced approaches to authoring a React app. ## General Rules 1. ๐Ÿšจ [Only use named exports](https://dev.to/n8io/developer-dark-arts-default-exports-31ia) 2. ๐Ÿ“ค Only `export` at the end of a file. Do not sprinkle them throughout the module like Hansel and Gretel. 3. ๐Ÿšจ Only use arrow functions 4. ๐Ÿ‘ฉโ€๐ŸŽ“ Only use functional React components. [Death to class components](https://dev.to/n8io/developer-dark-arts-react-class-components-3g8j). 5. ๐Ÿงช Spec files should be colocated to the implementation (and suffixed with `.spec.tsx`) * No `__tests__` directories * No `.test.tsx` suffix 6. ๐Ÿ“ค Only export from a module when there is a use case 7. ๐Ÿท๏ธ Import types via `import type { } from ...` syntax when possible 8. ๐Ÿ—„๏ธ If a component/data/util is used by multiple parents, move it up to a common parent directory 9. ๐ŸŽฃ All data requests should be made via `hook` 10. ๐Ÿ‘ทโ€โ™‚๏ธ Shared state should be shared via `Context` and accessed via `hook` 11. ๐Ÿซ React components should always be in the form of a `ProperCased` directory with child `index.tsx` and `index.spec.tsx` files * _All other file & directory names and should be `camelCased`_ 12. ๐Ÿฅ‡ React component files should contain only one component. Large components are fragile and hard to test. 13. ๐Ÿ”— You should always use a `react-router-dom` `<Link/>` for all links 14. โจฑ A variable name should never start with an underscore (`_`). The exception being that you want to keep an unused, positional function parameter as a placeholder to maintain readability. ```typescript= // For example... const removeUserMiddleware = (_request, response, next) => { response.body.user = undefined next() } ``` 11. ๐Ÿ”ก [Alphasort all the things](https://dev.to/n8io/developer-dark-arts-ralpha-sorting-c0o): destructured object keys, component props, imports, enum values, oh my! _If we have our linting/styling setup properly these will sort automatically when saving a file_ 12. ๐Ÿงถ [No magic strings/numbers](https://dev.to/n8io/developer-dark-arts-magic-strings-2ihn). These should be contained in an aptly-named variable(s) prior to consuming them. ## Testing ### Unit Tests 1. We should aim for 80% coverage 2. There should only be one snapshot test and it should be representative of the happy path of a component. Other conditional rendering should be tested using other means. E.g. `data-testid`'s or triggering events. 3. Snapshot tests should be inlined (via `.toMatchInlineSnapshot()`) _AND_ short (less than ~100 lines) 4. Where possible, child components should be mocked * Reduces the snapshot size * Promotes separation of concerns * Focuses tests to the given component's unit of work ### End to End Tests In an ideal world, **a feature should have at least one test**, possibly many more depending on its complexity. Good E2E tests should be checking if a particular set of interactions has a resulting outcomes that cannot be tested reliably by unit test. Their pass/fail outcome should answer the question "Is this feature working as expected?" ## Project Structure ### ๐Ÿ—ƒ๏ธ Root Directories #### `components` The place for React components that are shared across multiple modules. One directory per component, possibly with nested child components. #### `constants` Where you will find `enum`'s and frozen objects that are shared across multiple modules. One file per. #### `hooks` Where you will find all hooks that are shared across multiple modules. One directory per. #### `modules` 1. Each module directory represents an app page/view (excluding `App`) 2. Each module should have a `Routes/index.tsx` that controls top level and child routing 1. This is where you might see child route lazy loading if needed 3. Each module should export, at minimum, the `Routes` component Note: A general rule of thumb, if it is used across multiple modules, it needs to be elevated to a top level directory ##### The `modules/App` Directory This special module is needed to setup top level providers, contexts, styles, routing, and config. It is the entry point of the React app. #### `types` A place for types that are shared across multiple modules. One file per _logical model_. Multiple types can live in a single file so long as they share the same model/namespace. _NOTE: Don't forget, `enum`'s should live in `constants`_ #### `utils` The home of shared utility functions that are shared across multiple modules. One directory per. ### ๐Ÿšธ Child Directories Common child directories you _might_ see in `components` and `modules` root directories... 1. `components` 2. `constants` 3. `hooks` 4. `types` 5. `utils` 6. `Routes` - The component that controls nested view routing Note: These directories should only contain items that are scoped to the given module/component ### Import Aliases and Relative Pathing As a convenience, most projects allow aliased imports to avoid import statements with runaway relative paths. ```typescript= // โŒ import { thing } from '../../../../omg/make/it/stop/util/thing' // โœ… import { thing } from 'util/thing' ``` While this makes things much more readable, you can get yourself into trouble if you're not careful. #### TLDR; * If you are importing something **within** the current module, use the relative path (e.g. `'../../../util/thing'`). * If you are importing something **outside** the current module, use the aliased path (e.g. `'util/thing'`) #### The Long Story The `modules` root directory is a logical boundary between the "public apis" of each module. Each module's `index.tsx` should define all that is publicly accessible. ```typescript= // Given this... // modules/otherModule/util/index.tsx const util = () = '๐Ÿš€' export { util } // modules/otherModule/index.tsx export { util } from './util' ``` ```typescript= // โŒ Don't do this // modules/moduleA/Thing/index.tsx import { util } from 'modules/otherModule/util' ``` ```typescript= // โœ… Do this instead // modules/moduleA/Thing/index.tsx import { util } from 'modules/otherModule' ``` There are a few reasons why we would choose to follow this convention. 1. At a glance it should be easy to know what other modules might be using from the current module by simply opening up the module's `index.tsx` and reviewing the `export`'s therein. 2. Implicitly this helps us avoid and/or identify circular dependencies when they occur. * For example, `moduleC => depends on moduleB => depends on moduleA => depends on moduleC` is easier to reason about when there is a single entry point for each `module` * Circular dependencies are notoriously difficult to troubleshoot and can be easily avoided when we follow this rule.