--- title: Ghid de stil icon: paintbrush description: Convenții de cod și bune practici pentru a contribui la Twenty. --- ## React ### Doar componente funcționale Folosește întotdeauna componente funcționale TSX cu exporturi denumite. ```tsx // ❌ Bad const MyComponent = () => { return
Hello World
; }; export default MyComponent; // ✅ Good export function MyComponent() { return
Hello World
; }; ``` ### Proprietăți Creează un tip numit `{ComponentName}Props`. Folosește destructurarea. Nu folosi `React.FC`. ```tsx type MyComponentProps = { name: string; }; export const MyComponent = ({ name }: MyComponentProps) =>
Hello {name}
; ``` ### Fără spread al unei singure variabile de props ```tsx // ❌ Bad const MyComponent = (props: MyComponentProps) => ; // ✅ Good const MyComponent = ({ prop1, prop2 }: MyComponentProps) => ; ``` ## Managementul stării ### Atomi Jotai pentru starea globală ```tsx import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; export const myAtomState = createAtomState({ key: 'myAtomState', defaultValue: 'default value', }); ``` * Preferă atomii în locul prop drilling-ului * Nu folosi `useRef` pentru stare — folosește `useState` sau atomi * Folosește familii de atomi și selectori pentru liste ### Evită re-randări inutile * Extrage `useEffect` și preluarea datelor în componente surori de tip sidecar * Preferă handleri de evenimente (`handleClick`, `handleChange`) în locul lui `useEffect` * Nu folosi `React.memo()` — în schimb, rezolvă cauza principală * Limitează utilizarea `useCallback` / `useMemo` ```tsx // ❌ Bad — useEffect in the same component causes re-renders export const Page = () => { const [data, setData] = useAtomState(dataState); const [dep] = useAtomState(depState); useEffect(() => { setData(dep); }, [dep]); return
{data}
; }; // ✅ Good — extract into sibling export const PageData = () => { const [data, setData] = useAtomState(dataState); const [dep] = useAtomState(depState); useEffect(() => { setData(dep); }, [dep]); return <>; }; export const Page = () => { const [data] = useAtomState(dataState); return
{data}
; }; ``` ## TypeScript * **`type` în loc de `interface`** — mai flexibil, mai ușor de compus * **Șiruri literale în loc de enum-uri** — cu excepția enum-urilor generate de GraphQL codegen și a API-urilor interne ale bibliotecii * **Fără `any`** — TypeScript strict este impus * **Fără importuri de tip** — folosește importuri obișnuite (impus de Oxlint `typescript/consistent-type-imports`) * **Folosește [Zod](https://github.com/colinhacks/zod)** pentru validarea în timp de execuție a obiectelor netipizate ## JavaScript ```tsx // Use nullish-coalescing (??) instead of || const value = process.env.MY_VALUE ?? 'default'; // Use optional chaining onClick?.(); ``` ## Denumiri * **Variabile**: camelCase, descriptive (`email` nu `value`, `fieldMetadata` nu `fm`) * **Constante**: SCREAMING_SNAKE_CASE * **Tipuri/Clase**: PascalCase * **Fișiere/directoare**: kebab-case (`.component.tsx`, `.service.ts`, `.entity.ts`) * **Handleri de evenimente**: `handleClick` (nu `onClick` pentru funcția handler) * **Props de componentă**: prefixează cu numele componentei (`ButtonProps`) * **Componente stilizate**: prefixează cu `Styled` (`StyledTitle`) ## Stilizare Folosește componente stilizate [Linaria](https://github.com/callstack/linaria). Folosește valorile din temă — evită `px`, `rem` sau culori hardcodate. ```tsx // ❌ Bad const StyledButton = styled.button` color: #333333; font-size: 1rem; margin-left: 4px; `; // ✅ Good const StyledButton = styled.button` color: ${({ theme }) => theme.font.color.primary}; font-size: ${({ theme }) => theme.font.size.md}; margin-left: ${({ theme }) => theme.spacing(1)}; `; ``` ## Importuri Folosește aliasuri în locul căilor relative: ```tsx // ❌ Bad import { Foo } from '../../../../../testing/decorators/Foo'; // ✅ Good import { Foo } from '~/testing/decorators/Foo'; import { Bar } from '@/modules/bar/components/Bar'; ``` ## Structura folderelor ``` front └── modules/ # Feature modules │ └── module1/ │ ├── components/ │ ├── constants/ │ ├── contexts/ │ ├── graphql/ (fragments, queries, mutations) │ ├── hooks/ │ ├── states/ (atoms, selectors) │ ├── types/ │ └── utils/ └── pages/ # Route-level components └── ui/ # Reusable UI components (display, input, feedback, ...) ``` * Modulele pot importa din alte module, dar `ui/` ar trebui să rămână fără dependențe * Folosește subfoldere `internal/` pentru cod privat modulului * Componente sub 300 de linii, servicii sub 500 de linii