Created by Github action --------- Co-authored-by: Crowdin Bot <support+bot@crowdin.com> Co-authored-by: github-actions <github-actions@twenty.com>
334 lines
5.9 KiB
Plaintext
334 lines
5.9 KiB
Plaintext
---
|
|
title: crwdns60144:0crwdne60144:0
|
|
image: crwdns60146:0crwdne60146:0
|
|
---
|
|
|
|
<Frame>
|
|
<img src="/images/user-guide/tips/light-bulb.png" alt="Header" />
|
|
</Frame>
|
|
|
|
crwdns60148:0crwdne60148:0
|
|
|
|
## crwdns60150:0crwdne60150:0
|
|
|
|
crwdns60152:0crwdne60152:0
|
|
|
|
### crwdns60154:0crwdne60154:0
|
|
|
|
crwdns60156:0crwdne60156:0
|
|
|
|
<Warning>
|
|
|
|
crwdns60158:0crwdne60158:0
|
|
|
|
</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>
|
|
);
|
|
}
|
|
```
|
|
|
|
### crwdns60160:0crwdne60160:0
|
|
|
|
crwdns60162:0crwdne60162:0
|
|
|
|
crwdns60164:0crwdne60164:0
|
|
|
|
crwdns60166:0#managing-re-renderscrwdne60166:0
|
|
|
|
## crwdns60168:0crwdne60168:0
|
|
|
|
crwdns60170:0crwdne60170:0
|
|
|
|
crwdns60172:0crwdne60172:0
|
|
|
|
crwdns60174:0crwdne60174:0
|
|
|
|
### crwdns60176:0crwdne60176:0
|
|
|
|
crwdns60178:0crwdne60178:0
|
|
|
|
crwdns60180:0crwdne60180:0
|
|
|
|
crwdns60182:0crwdne60182:0
|
|
|
|
### crwdns60184:0crwdne60184:0
|
|
|
|
crwdns60186:0crwdne60186:0
|
|
|
|
crwdns60188:0crwdne60188:0
|
|
|
|
crwdns60190:0crwdne60190:0
|
|
|
|
crwdns60192:0crwdne60192:0
|
|
|
|
### crwdns60194:0crwdne60194:0
|
|
|
|
crwdns60196:0crwdne60196:0
|
|
|
|
crwdns60198:0crwdne60198:0
|
|
|
|
```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>
|
|
);
|
|
```
|
|
|
|
### crwdns60200:0crwdne60200:0
|
|
|
|
crwdns60202:0crwdne60202:0
|
|
|
|
crwdns60204:0crwdne60204:0
|
|
|
|
### crwdns60206:0crwdne60206:0
|
|
|
|
crwdns60208:0crwdne60208:0
|
|
|
|
### crwdns60210:0crwdne60210:0
|
|
|
|
crwdns60212:0crwdne60212:0
|
|
|
|
## crwdns60214:0crwdne60214:0
|
|
|
|
crwdns60216:0crwdne60216:0 crwdns60218:0crwdne60218:0
|
|
|
|
1. crwdns60220:0crwdne60220:0
|
|
|
|
2. crwdns60222:0crwdne60222:0
|
|
|
|
3. crwdns60224:0crwdne60224:0
|
|
|
|
4. crwdns60226:0crwdne60226:0
|
|
|
|
crwdns60228:0crwdne60228:0
|
|
|
|
## crwdns60230:0crwdne60230:0
|
|
|
|
### crwdns60232:0crwdne60232:0
|
|
|
|
crwdns60234:0crwdne60234:0
|
|
|
|
#### crwdns60236:0crwdne60236:0
|
|
|
|
crwdns60238:0crwdne60238:0 crwdns60240:0crwdne60240:0 crwdns60242:0crwdne60242:0 crwdns60244:0crwdne60244:0
|
|
|
|
```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('');
|
|
```
|
|
|
|
#### crwdns60246:0crwdne60246:0
|
|
|
|
- crwdns60248:0crwdne60248:0
|
|
|
|
### crwdns60250:0crwdne60250:0
|
|
|
|
crwdns60252:0crwdne60252:0
|
|
|
|
```tsx
|
|
// ❌ Bad
|
|
const onEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
```tsx
|
|
// ✅ Good
|
|
const handleEmailChange = (val: string) => {
|
|
// ...
|
|
};
|
|
```
|
|
|
|
## crwdns60254:0crwdne60254:0
|
|
|
|
crwdns60256:0crwdne60256:0
|
|
|
|
crwdns60258:0crwdne60258:0
|
|
|
|
crwdns60260:0crwdne60260:0
|
|
|
|
```tsx
|
|
type EmailFieldProps = {
|
|
value: string;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
|
|
<TextInput value={value} disabled={disabled} fullWidth />
|
|
);
|
|
```
|
|
|
|
crwdns60262:0crwdne60262:0
|
|
|
|
```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" />;
|
|
```
|
|
|
|
## crwdns60264:0crwdne60264:0
|
|
|
|
crwdns60266:0crwdne60266:0
|
|
|
|
crwdns60268:0crwdne60268:0
|
|
|
|
```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>
|
|
)
|
|
};
|
|
```
|
|
|
|
crwdns60270:0crwdne60270:0
|
|
|
|
## crwdns60272:0crwdne60272:0
|
|
|
|
crwdns60274:0crwdne60274:0 crwdns60276:0crwdne60276:0
|
|
|
|
1. crwdns60278:0crwdne60278:0
|
|
|
|
2. crwdns60280:0crwdne60280:0
|
|
|
|
3. crwdns60282:0crwdne60282:0
|
|
|
|
crwdns60284:0#state-managementcrwdne60284:0
|
|
|
|
## crwdns60286:0crwdne60286:0
|
|
|
|
crwdns60288:0crwdne60288:0
|
|
|
|
crwdns60290:0crwdne60290:0
|
|
|
|
```js
|
|
{
|
|
alias: {
|
|
"~": path.resolve(__dirname, "src"),
|
|
"@": path.resolve(__dirname, "src/modules"),
|
|
"@testing": path.resolve(__dirname, "src/testing"),
|
|
},
|
|
}
|
|
```
|
|
|
|
crwdns60292:0crwdne60292:0
|
|
|
|
```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';
|
|
```
|
|
|
|
## crwdns60294:0crwdne60294:0
|
|
|
|
crwdns60296:0https://github.com/colinhacks/zodcrwdne60296:0
|
|
|
|
```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>;
|
|
```
|
|
|
|
## crwdns60298:0crwdne60298:0
|
|
|
|
crwdns60300:0crwdne60300:0
|
|
|