8cadd00d34
Created by Github action --------- Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: github-actions <github-actions@twenty.com>
334 lines
9.8 KiB
Plaintext
334 lines
9.8 KiB
Plaintext
---
|
|
title: Bedste praksis
|
|
image: /images/user-guide/tips/light-bulb.png
|
|
---
|
|
|
|
<Frame>
|
|
<img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
|
|
</Frame>
|
|
|
|
Dette dokument beskriver de bedste praksis, som du bør følge, når du arbejder på frontend.
|
|
|
|
## Tilstandshåndtering
|
|
|
|
React og Recoil håndterer tilstandshåndtering i kodebasen.
|
|
|
|
### Brug `useRecoilState` til at gemme tilstand
|
|
|
|
Det er god praksis at oprette så mange atomer, som du har brug for til at gemme din tilstand.
|
|
|
|
<Warning>
|
|
|
|
Det er bedre at bruge ekstra atomer end at prøve at være for kortfattet med prop-drilling.
|
|
|
|
</Warning>
|
|
|
|
```tsx
|
|
export const myAtomState = atom({
|
|
key: 'myAtomState',
|
|
default: 'default value',
|
|
});
|
|
|
|
export const MyComponent = () => {
|
|
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
|
|
|
return (
|
|
<div>
|
|
<input
|
|
value={myAtom}
|
|
onChange={(e) => setMyAtom(e.target.value)}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Brug ikke `useRef` til at gemme tilstand
|
|
|
|
Undgå brug af `useRef` til at gemme tilstand.
|
|
|
|
Hvis du vil gemme tilstand, skal du bruge `useState` eller `useRecoilState`.
|
|
|
|
Se [hvordan man håndterer re-renders](#managing-re-renders) hvis du føler, at du har brug for `useRef` til at forhindre nogle re-renders fra at ske.
|
|
|
|
## Håndtering af re-renders
|
|
|
|
Re-renders kan være svære at håndtere i React.
|
|
|
|
Her er nogle regler at følge for at undgå unødvendige re-renders.
|
|
|
|
Husk, at du **altid** kan undgå re-renders ved at forstå deres årsag.
|
|
|
|
### Arbejd på rodniveau
|
|
|
|
Undgåelse af re-renders i nye funktioner bliver nu lettere ved at eliminere dem på rodniveau.
|
|
|
|
`PageChangeEffect` sidecar komponenten indeholder kun én `useEffect` der rummer al logik til at udføre ved en sideændring.
|
|
|
|
På den måde ved du, at der kun er ét sted, der kan udløse en re-render.
|
|
|
|
### Tænk altid to gange, før du tilføjer `useEffect` i din kodebase
|
|
|
|
Re-renders er ofte forårsaget af unødvendige `useEffect`.
|
|
|
|
Du bør overveje, om du har brug for `useEffect`, eller om du kan flytte logikken i en event handler funktion.
|
|
|
|
Det er generelt nemt at flytte logikken i en `handleClick` eller `handleChange` funktion.
|
|
|
|
Du kan også finde dem i biblioteker som Apollo: `onCompleted`, `onError`, osv.
|
|
|
|
### Use a sibling component to extract `useEffect` or data fetching logic
|
|
|
|
Hvis du føler, at du har brug for at tilføje en `useEffect` i din root komponent, bør du overveje at udtrække den i en sidecar komponent.
|
|
|
|
Du kan anvende det samme for dataindsamlingslogik, med Apollo hooks.
|
|
|
|
```tsx
|
|
// ❌ Bad, will cause re-renders even if data is not changing,
|
|
// because useEffect needs to be re-evaluated
|
|
export const PageComponent = () => {
|
|
const [data, setData] = useRecoilState(dataState);
|
|
const [someDependency] = useRecoilState(someDependencyState);
|
|
|
|
useEffect(() => {
|
|
if(someDependency !== data) {
|
|
setData(someDependency);
|
|
}
|
|
}, [someDependency]);
|
|
|
|
return <div>{data}</div>;
|
|
};
|
|
|
|
export const App = () => (
|
|
<RecoilRoot>
|
|
<PageComponent />
|
|
</RecoilRoot>
|
|
);
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, will not cause re-renders if data is not changing,
|
|
// because useEffect is re-evaluated in another sibling component
|
|
export const PageComponent = () => {
|
|
const [data, setData] = useRecoilState(dataState);
|
|
|
|
return <div>{data}</div>;
|
|
};
|
|
|
|
export const PageData = () => {
|
|
const [data, setData] = useRecoilState(dataState);
|
|
const [someDependency] = useRecoilState(someDependencyState);
|
|
|
|
useEffect(() => {
|
|
if(someDependency !== data) {
|
|
setData(someDependency);
|
|
}
|
|
}, [someDependency]);
|
|
|
|
return <></>;
|
|
};
|
|
|
|
export const App = () => (
|
|
<RecoilRoot>
|
|
<PageData />
|
|
<PageComponent />
|
|
</RecoilRoot>
|
|
);
|
|
```
|
|
|
|
### Brug recoil familietilstande og recoil familieselectors
|
|
|
|
Recoil familietilstande og selectors er en god måde at undgå re-renders.
|
|
|
|
De er nyttige, når du har brug for at gemme en liste af elementer.
|
|
|
|
### Du bør ikke bruge `React.memo(MyComponent)`
|
|
|
|
Undgå brugen af `React.memo()` fordi det ikke løser årsagen til re-render, men i stedet bryder re-render kæden, hvilket kan føre til uventet adfærd og gøre koden meget svær at refaktorere.
|
|
|
|
### Begræns brugen af `useCallback` eller `useMemo`
|
|
|
|
De er ofte unødvendige og gør koden sværere at læse og vedligeholde for en præstationsgevinst der er umærkelig.
|
|
|
|
## Console.logs
|
|
|
|
`console.log` udsagn er værdifulde under udvikling, og giver realtid indsigt i variabelværdier og kodeflow. Men at efterlade dem i produktionskode kan føre til flere problemer:
|
|
|
|
1. **Ydelse**: Overdreven logning kan påvirke ydelsen køretiden, især i klient-side applikationer.
|
|
|
|
2. **Sikkerhed**: Logning af følsomme data kan afsløre kritiske oplysninger for enhver, der inspicerer browserens konsol.
|
|
|
|
3. **Renlighed**: Fyldning af konsollen med logs kan skjule vigtige advarsler eller fejl, som udviklere eller værktøjer har brug for at se.
|
|
|
|
4. **Professionalitet**: Slutbrugere eller klienter, der ser konsollen og ser en overflod af log udsagn, kan stille spørgsmålstegn ved kodens kvalitet og finish.
|
|
|
|
Sørg for, at du fjerner alle `console.logs` før du laver kode til produktion.
|
|
|
|
## Navngivning
|
|
|
|
### Variabelnavngivning
|
|
|
|
Variabelnavne skal nøjagtigt beskrive variablens formål eller funktion.
|
|
|
|
#### Problemet med generiske navne
|
|
|
|
Generiske navne i programmering er ikke ideelle, fordi de mangler specificitet, hvilket fører til uklarhed og reduceret kodens læsbarhed. Sådanne navne formår ikke at formidle variablen eller funktionens formål, hvilket gør det udfordrende for udviklere at forstå kodens hensigt uden dybere undersøgelse. Dette kan resultere i øget fejlfindingstid, højere tendens til fejl og vanskeligheder i vedligeholdelse og samarbejde. Mens beskrivende navngivning gør koden selvforklarende og lettere at navigere, hvilket forbedrer kodekvalitet og udviklerproduktivitet.
|
|
|
|
```tsx
|
|
// ❌ Bad, uses a generic name that doesn't communicate its
|
|
// purpose or content clearly
|
|
const [value, setValue] = useState('');
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, uses a descriptive name
|
|
const [email, setEmail] = useState('');
|
|
```
|
|
|
|
#### Nogle ord at undgå i variabelnavne
|
|
|
|
- dummy
|
|
|
|
### Eventhåndterere
|
|
|
|
Eventhåndterernavne bør starte med `handle`, mens `on` er en præfiks brugt til at navngive events i component props.
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
const onEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good
|
|
const handleEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
## Valgfri props
|
|
|
|
Undgå at passere standardværdien for en valgfri prop.
|
|
|
|
**EKSEMPEL**
|
|
|
|
Tag `EmailField` komponenten defineret nedenfor:
|
|
|
|
```tsx
|
|
type EmailFieldProps = {
|
|
value: string;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
|
|
<TextInput value={value} disabled={disabled} fullWidth />
|
|
);
|
|
```
|
|
|
|
**Brug**
|
|
|
|
```tsx
|
|
// ❌ Bad, passing in the same value as the default value adds no value
|
|
const Form = () => <EmailField value="username@email.com" disabled={false} />;
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, assumes the default value
|
|
const Form = () => <EmailField value="username@email.com" />;
|
|
```
|
|
|
|
## Komponent som props
|
|
|
|
Try as much as possible to pass uninstantiated components as props, so children can decide on their own of what props they need to pass.
|
|
|
|
Det mest almindelige eksempel på dette er ikonkomponenter:
|
|
|
|
```tsx
|
|
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
|
|
|
|
// In MyComponent
|
|
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
|
|
const theme = useTheme();
|
|
|
|
return (
|
|
<div>
|
|
<MyIcon size={theme.icon.size.md}>
|
|
</div>
|
|
)
|
|
};
|
|
```
|
|
|
|
For at React forstår, at komponenten er en komponent, skal du bruge PascalCase, for senere at kunne instantiere den med `<MyIcon>`
|
|
|
|
## Prop Drilling: Hold det minimalt
|
|
|
|
Prop drilning, i React sammenhæng, refererer til praksis med at passere state variabler og deres setters gennem mange lag af komponenter, selv hvis mellemliggende komponenter ikke anvender dem. Mens det nogle gange er nødvendigt, kan det også medføre:
|
|
|
|
1. **Nedsat Læsbarhed**: At sporere hvor en prop oprinder fra, eller hvor den anvendes, kan blive indviklet i en dybt indlejret komponentstruktur.
|
|
|
|
2. **Vedligeholdelsesudfordringer**: Ændringer i en komponents prop-struktur kan kræve justeringer i flere komponenter, selv hvis de ikke direkte anvender prop'en.
|
|
|
|
3. **Reduceret Komponent Genanvendelighed**: En komponent, der modtager mange props kun for at passere dem videre, bliver mindre almen og sværere at genbruge i forskellige sammenhænge.
|
|
|
|
Hvis du føler, at du bruger overdreven prop drilning, se [best practices for state management](#state-management).
|
|
|
|
## Importering
|
|
|
|
Når du importerer, vælg de udpegede aliasser i stedet for at specificere komplette eller relative stier.
|
|
|
|
**Aliasserne**
|
|
|
|
```js
|
|
{
|
|
alias: {
|
|
"~": path.resolve(__dirname, "src"),
|
|
"@": path.resolve(__dirname, "src/modules"),
|
|
"@testing": path.resolve(__dirname, "src/testing"),
|
|
},
|
|
}
|
|
```
|
|
|
|
**Brug**
|
|
|
|
```tsx
|
|
// ❌ Bad, specifies the entire relative path
|
|
import {
|
|
CatalogDecorator
|
|
} from '../../../../../testing/decorators/CatalogDecorator';
|
|
import {
|
|
ComponentDecorator
|
|
} from '../../../../../testing/decorators/ComponentDecorator';
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good, utilises the designated aliases
|
|
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
|
import { ComponentDecorator } from 'twenty-ui/testing';
|
|
```
|
|
|
|
## Skema Validering
|
|
|
|
[Zod](https://github.com/colinhacks/zod) er skemavalideringen for utyped objekter:
|
|
|
|
```js
|
|
const validationSchema = z
|
|
.object({
|
|
exist: z.boolean(),
|
|
email: z
|
|
.string()
|
|
.email('Email must be a valid email'),
|
|
password: z
|
|
.string()
|
|
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
|
|
})
|
|
.required();
|
|
|
|
type Form = z.infer<typeof validationSchema>;
|
|
```
|
|
|
|
## Breaking Changes
|
|
|
|
Udfør altid grundig manuel testning, før du fortsætter for at sikre, at ændringerne ikke har forårsaget forstyrrelser andre steder, givet at test endnu ikke er bredt integreret.
|
|
|