From 712d1c6fa4025bc40a9d816326f2826fffa42c98 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:12:09 +0200 Subject: [PATCH] ADR: Use of conditionals in JSX (``) (#8025) Co-authored-by: Melinda Fekete --- website/docs/contributing/ADRs/ADRs.md | 1 + .../ADRs/front-end/jsx-conditionals.md | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 website/docs/contributing/ADRs/front-end/jsx-conditionals.md diff --git a/website/docs/contributing/ADRs/ADRs.md b/website/docs/contributing/ADRs/ADRs.md index ff307054b1..b23a6fe6e9 100644 --- a/website/docs/contributing/ADRs/ADRs.md +++ b/website/docs/contributing/ADRs/ADRs.md @@ -43,3 +43,4 @@ We have created a set of ADRs to help guide the development of the front end: * [Preferred folder structure](./front-end/preferred-folder-structure.md) * [Preferred form architecture](./front-end/preferred-form-architecture.md) * [OpenAPI SDK generator](./front-end/sdk-generator.md) +* [Use of conditionals in JSX (refactor of <ConditionallyRender />)](./front-end/jsx-conditionals.md) diff --git a/website/docs/contributing/ADRs/front-end/jsx-conditionals.md b/website/docs/contributing/ADRs/front-end/jsx-conditionals.md new file mode 100644 index 0000000000..9fb3e3cc5f --- /dev/null +++ b/website/docs/contributing/ADRs/front-end/jsx-conditionals.md @@ -0,0 +1,121 @@ +--- +title: "ADR: Use of conditionals in JSX (deprecation of ``)" +--- + +## Background + +Using the `&&` operator in React can lead to unexpected rendering behavior when dealing with certain falsy values. In our codebase, the `` component has been used to render React elements based on a boolean condition. However, it has certain drawbacks, which is why we would like to replace it with the ternary operator. + +### Pitfalls of `&&` operator + +While most truthy and falsy values behave as expected with the `&&` operator, certain falsy values can produce unintended outcomes: + +```tsx +{NaN &&

} // will render `NaN` +{0 &&

} // will render `0` +{arr?.length &&

} // can render `0` +``` + +These issues can cause bugs in components that conditionally render UI elements based on numeric values or other potentially falsy conditions. For this reason, we use a wrapper. + +```tsx +

} +/> +``` + +### What's wrong with `` + +While this solves leaking render issues, it has some drawbacks. + +#### Poor TypeScript support +```tsx +import type { FC } from 'react'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; + +const SubComponent: FC<{ text: string }> = ({ text }) => <>{text}; + +export const Test: FC<{ maybeString?: string }> = ({ maybeString }) => ( + } + // ❌ TS Error: Type 'string | undefined' is not assignable to type 'string' + // you have to use `maybeString!` here + /> +); +``` + +#### Obfuscation of code smells and code cruft +Nested ternaries are easier to spot than nested `` elements. + +```tsx +
+ This is bad

} + /> + )} + elseShow={'Should be refactored'} + /> +
+``` + +## Options considered + +To avoid these issues, safer alternatives to the `&&` operator can be used: + +### **Convert to boolean** +We could try to explicitly convert the condition to a boolean, ensuring that only `true` or `false` determine the rendering. + +```tsx +{Boolean(NaN) &&

} // Won't render anything +{!!0 &&

} // Also safe +``` + +**Unfortunately** Biome (the linter we use) does not include rules to automatically enforce a safer usage of the `&&` operator, as ESLint did. + +### Ternary Operator +The ternary operator is a more explicit and safer approach. This covers cases where we need to return `null` or `undefined`. + +``` tsx +{NaN ?

👍

: null} // Won't render anything +``` + +It also plays nicely with TypeScript. + +```tsx +export const Test: FC<{ maybeString?: string }> = ({ maybeString }) => + maybeString ? : null; +``` + +This is what we will use from now onwards. + +## Consequences +Positive: The codebase will become more type-safe and easier to understand. + +Negative: The `` component is imported in nearly 400 files. Significant refactoring effort is required. + +Performance: There was no measurable performance difference between code with and without this component. This was tested on production bundle, on the features search (table) and projects list pages. + +## Migration plan + +1. Mark `` as deprecated in the codebase with a clear JSDoc comment. + +2. Automated refactoring with AST (Abstract Syntax Tree): +There already is a script developed that can convert files between `ConditionallyRender` and ternary syntax. It uses jscodeshift, a library. It will be put in `frontend/scripts/transform.js`. + +3. Each change will have to be reviewed. The order of refactoring should be: + 1. New features that are behind feature flags. + 2. Non-critical or not in demand pages, like new signals or feedback component. + 3. Less complex pages, for example in `/src/component/admin`. + 4. More complex and critical pages, like strategy editing. + 5. Utilities and components used in many places (`/src/component/common`). + +3. Once all instances of `` have been refactored, remove the component from the codebase.