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: Beste praksis
|
|
image: /images/user-guide/tips/light-bulb.png
|
|
---
|
|
|
|
<Frame>
|
|
<img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
|
|
</Frame>
|
|
|
|
Dette dokumentet skisserer de beste praksisene du bør følge når du jobber på frontend.
|
|
|
|
## Tilstandshåndtering
|
|
|
|
React og Recoil håndterer tilstandshåndtering i kodebasen.
|
|
|
|
### Bruk `useRecoilState` for å lagre tilstand
|
|
|
|
Det er en god praksis å lage så mange atomer som du trenger for å lagre tilstanden din.
|
|
|
|
<Warning>
|
|
|
|
Det er bedre å bruke ekstra atomer enn å prøve å være for konsis med å bore props.
|
|
|
|
</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>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Ikke bruk `useRef` for å lagre tilstand
|
|
|
|
Unngå å bruke `useRef` for å lagre tilstand.
|
|
|
|
Hvis du vil lagre tilstand, bør du bruke `useState` eller `useRecoilState`.
|
|
|
|
Se [hvordan håndtere omrendringer](#managing-re-renders) hvis du føler at du trenger `useRef` for å forhindre at noen omrendringer skjer.
|
|
|
|
## Håndtere omrendringer
|
|
|
|
Omrendringer kan være vanskelige å håndtere i React.
|
|
|
|
Her er noen regler å følge for å unngå unødvendige omrendringer.
|
|
|
|
Husk at du **alltid** kan unngå omrendringer ved å forstå deres årsak.
|
|
|
|
### Arbeid på rotnivå
|
|
|
|
Unngåelse av omrendringer i nye funksjoner er nå gjort enkelt ved å eliminere dem på rotnivå.
|
|
|
|
The `PageChangeEffect` sidecar component contains just one `useEffect` that holds all the logic to execute on a page change.
|
|
|
|
På denne måten vet du at det er bare ett sted som kan utløse en omrendering.
|
|
|
|
### Tenk deg nøye om før du legger til `useEffect` i din kodebase
|
|
|
|
Omrendringer er ofte forårsaket av unødvendig `useEffect`.
|
|
|
|
Du bør vurdere om du trenger `useEffect`, eller om du kan flytte logikken til en hendelseshåndtererfunksjon.
|
|
|
|
Du vil generelt finne det enkelt å flytte logikken til en `handleClick` eller `handleChange`-funksjon.
|
|
|
|
Du kan også finne dem i biblioteker som Apollo: `onCompleted`, `onError`, osv.
|
|
|
|
### Bruk en søskenkomponent for å trekke ut `useEffect` eller datahentingslogikk
|
|
|
|
Hvis du føler at du trenger å legge til en `useEffect` i din rotkomponent, bør du vurdere å trekke den ut i en sidevognkomponent.
|
|
|
|
Du kan bruke det samme for datahentingslogikk, 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>
|
|
);
|
|
```
|
|
|
|
### Bruk recoil familietilstander og recoil familieselektorer
|
|
|
|
Recoil familietilstander og selektorer er en flott måte å unngå omrendringer på.
|
|
|
|
De er nyttige når du trenger å lagre en liste over elementer.
|
|
|
|
### Du bør ikke bruke `React.memo(MyComponent)`
|
|
|
|
Unngå bruk av `React.memo()` fordi det ikke løser årsaken til omrenderingen, men i stedet bryter omrenderingskjeden, noe som kan føre til uventet oppførsel og gjøre koden veldig vanskelig å refaktorere.
|
|
|
|
### Begrens bruken av `useCallback` eller `useMemo`
|
|
|
|
De er ofte ikke nødvendige og vil gjøre koden vanskeligere å lese og vedlikeholde for en gevinst i ytelse som er umerkelig.
|
|
|
|
## Console.logs
|
|
|
|
`console.log` setninger er verdifulle under utvikling, og tilbyr sanntidsinnsikt i variabelverdier og kodeflyt. Men å la dem være i produksjonskode kan føre til flere problemer:
|
|
|
|
1. **Ytelse**: Overdreven logging kan påvirke kjøringsytelsen, spesielt på klientapplikasjoner.
|
|
|
|
2. **Sikkerhet**: Logging av sensitiv data kan avsløre kritisk informasjon til alle som inspiserer nettleserens konsoll.
|
|
|
|
3. **Ryddighet**: Å fylle opp konsollen med logger kan skjule viktige advarsler eller feil som utviklere eller verktøy trenger å se.
|
|
|
|
4. **Profesjonalitet**: Sluttbrukere eller kunder som sjekker konsollen og ser en mengde loggutsagn kan stille spørsmål ved kvaliteten og finishen av koden.
|
|
|
|
Forsikre deg om at du fjerner alle `console.logs` før du skyver koden til produksjon.
|
|
|
|
## Navngiving
|
|
|
|
### Variabelnavngiving
|
|
|
|
Variabelnavn bør presist beskrive variablens hensikt eller funksjon.
|
|
|
|
#### Problemet med generiske navn
|
|
|
|
Generiske navn i programmering er ikke ideelle fordi de mangler spesifisitet, noe som fører til tvetydighet og redusert kodelesbarhet. Slike navn klarer ikke å formidle variabelen eller funksjonens hensikt, noe som gjør det utfordrende for utviklere å forstå kodenes intensjon uten dypere undersøkelser. Dette kan føre til økt feilsøkingstid, høyere sårbarhet for feil, og vansker ved vedlikehold og samarbeid. I mellomtiden gjør beskrivende navngiving koden selvforklarende og lettere å navigere, og øker kodekvaliteten og utviklerens produktivitet.
|
|
|
|
```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('');
|
|
```
|
|
|
|
#### Noen ord å unngå i variabelnavn
|
|
|
|
- dummy
|
|
|
|
### Hendelseshåndterere
|
|
|
|
Hendelseshåndterernes navn bør starte med `handle`, mens `on` er et prefiks brukt for å navngi hendelser i komponentprops.
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
const onEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good
|
|
const handleEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
## Valgfrie Props
|
|
|
|
Unngå å sende den standardverdien for en valgfri prop.
|
|
|
|
**EKSEMPEL**
|
|
|
|
Ta `EmailField` komponenten definert nedenfor:
|
|
|
|
```tsx
|
|
type EmailFieldProps = {
|
|
value: string;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
|
|
<TextInput value={value} disabled={disabled} fullWidth />
|
|
);
|
|
```
|
|
|
|
**Bruk**
|
|
|
|
```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
|
|
|
|
Prøv så mye som mulig å sende ikke-instanserte komponenter som props, slik at barn kan bestemme selv hvilke props de trenger å sende.
|
|
|
|
Det vanligste eksemplet på dette er ikonekomponenter.
|
|
|
|
```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 skal forstå at komponenten er en komponent, må du bruke store bokstaver, for senere å instansiere den med `<MyIcon>`
|
|
|
|
## Prop-gjennomgang: Hold det minimalt
|
|
|
|
I React-sammenheng refererer prop-gjennomgang til praksisen med å sende tilstandsvariabler og deres settere gjennom mange komponentlag, selv om mellomliggende komponenter ikke bruker dem. Selv om det noen ganger er nødvendig, kan overdreven prop-gjennomgang føre til:
|
|
|
|
1. **Redusert Lesbarhet**: Å spore hvor en prop stammer fra eller hvor den brukes kan bli komplisert i en dypt nestet komponentstruktur.
|
|
|
|
2. **Vedlikeholdsutfordringer**: Endringer i en komponents prop-struktur kan kreve justeringer i flere komponenter, selv om de ikke direkte bruker propen.
|
|
|
|
3. **Redusert Komponentgjenbruk**: En komponent som mottar mange props kun for å sende dem videre blir mindre allsidig og vanskeligere å gjenbruke i forskjellige sammenhenger.
|
|
|
|
Hvis du føler at du bruker overdreven prop-gjennomgang, se [beste praksis for tilstandshåndtering](#state-management).
|
|
|
|
## Importer
|
|
|
|
Ved import, velg de angitte aliasene fremfor å spesifisere komplette eller relative stier.
|
|
|
|
**Håndtak Aliaser**
|
|
|
|
```js
|
|
{
|
|
alias: {
|
|
"~": path.resolve(__dirname, "src"),
|
|
"@": path.resolve(__dirname, "src/modules"),
|
|
"@testing": path.resolve(__dirname, "src/testing"),
|
|
},
|
|
}
|
|
```
|
|
|
|
**Bruk**
|
|
|
|
```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';
|
|
```
|
|
|
|
## Schema-validering
|
|
|
|
[Zod](https://github.com/colinhacks/zod) er skjema-valideren for utypede 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>;
|
|
```
|
|
|
|
## Kritiske Endringer
|
|
|
|
Utfør alltid grundig manuell testing før du går videre for å sikre at modifikasjoner ikke har forårsaket forstyrrelser andre steder, gitt at tester ennå ikke er omfattende integrert.
|
|
|