Skip to content

Commit 54824f0

Browse files
committed
fix: add useValueHasChanged hook
1 parent dea105c commit 54824f0

File tree

6 files changed

+42
-26
lines changed

6 files changed

+42
-26
lines changed

Diff for: src/components/DayPicker/hooks/useDayPickerInputManagement.ts

+7-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { ChangeEvent, ChangeEventHandler, useEffect, useState } from 'react';
1+
import { ChangeEvent, ChangeEventHandler, useState } from 'react';
22

33
import dayjs from 'dayjs';
44

55
import { DATE_FORMAT } from '@/components/DayPicker/constants';
66
import { parseInputToDate } from '@/components/DayPicker/parseInputToDate';
7+
import { useValueHasChanged } from '@/hooks/useValueHasChanged';
78

89
type UseDayPickerInputManagement = {
910
inputValue: string;
@@ -27,19 +28,12 @@ export const useDayPickerInputManagement = (
2728
);
2829

2930
const dateValueAsDayjs = dayjs(dateValue);
31+
const dateFormatHasChanged = useValueHasChanged(dateFormat);
32+
const dateValueHasChanged = useValueHasChanged(dateValue);
3033

31-
// Pour mettre à jour l'input selon la value
32-
useEffect(() => {
33-
if (!!dateValue) {
34-
// TODO @eslint-react rule
35-
// eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
36-
setInputValue(dayjs(dateValue).format(dateFormat));
37-
} else {
38-
// TODO @eslint-react rule
39-
// eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
40-
setInputValue('');
41-
}
42-
}, [dateFormat, dateValue]);
34+
if (dateFormatHasChanged || dateValueHasChanged) {
35+
setInputValue(dateValue ? dayjs(dateValue).format(dateFormat) : '');
36+
}
4337

4438
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
4539
setInputValue(e.currentTarget.value);

Diff for: src/components/MonthPicker/YearContext.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
useState,
99
} from 'react';
1010

11+
import { useValueHasChanged } from '@/hooks/useValueHasChanged';
12+
1113
type YearContextType = {
1214
year: number;
1315
setYear: Dispatch<SetStateAction<number>>;
@@ -28,12 +30,11 @@ export const YearProvider: React.FC<
2830
React.PropsWithChildren<YearProviderProp>
2931
> = ({ year: yearProp, onYearChange, children }) => {
3032
const [year, setYear] = useState(yearProp);
33+
const yearHasChanged = useValueHasChanged(yearProp);
3134

32-
useEffect(() => {
33-
// TODO @eslint-react rule
34-
// eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
35+
if (yearHasChanged && yearProp !== year) {
3536
setYear(yearProp);
36-
}, [yearProp]);
37+
}
3738

3839
useEffect(() => {
3940
onYearChange?.(year);

Diff for: src/components/MonthPicker/docs.stories.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react';
22

3-
import { Box } from '@chakra-ui/react';
3+
import { Box, Button, Stack } from '@chakra-ui/react';
44

55
import { MonthPicker } from '.';
66

@@ -18,6 +18,16 @@ export const Wrapper = () => (
1818

1919
export const InitialYear = () => <MonthPicker year={1994} />;
2020

21+
export const UpdateYearFromOutside = () => {
22+
const [year, setYear] = useState(1994);
23+
return (
24+
<Stack>
25+
<MonthPicker year={year} onYearChange={(y) => setYear(y)} />
26+
<Button onClick={() => setYear((y) => y + 1)}>Increment Year</Button>
27+
</Stack>
28+
);
29+
};
30+
2131
export const SelectedMonth = () => (
2232
<MonthPicker year={2021} selectedMonths={[new Date(2021, 7)]} />
2333
);

Diff for: src/components/SearchInput/docs.stories.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState } from 'react';
22

3-
import { Stack, Text } from '@chakra-ui/react';
3+
import { Button, Stack, Text } from '@chakra-ui/react';
44

55
import { SearchInput } from '.';
66

@@ -19,6 +19,9 @@ export const Controlled = () => {
1919
<Stack spacing={4}>
2020
<SearchInput value={value} onChange={setValue} />
2121
<Text>{value}</Text>
22+
<Button onClick={() => setValue(Math.random().toString())}>
23+
Update value from outside
24+
</Button>
2225
</Stack>
2326
);
2427
};

Diff for: src/components/SearchInput/index.tsx

+6-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
import { useTranslation } from 'react-i18next';
1515
import { LuSearch, LuX } from 'react-icons/lu';
1616

17+
import { useValueHasChanged } from '@/hooks/useValueHasChanged';
18+
1719
type CustomProps = {
1820
value?: string;
1921
defaultValue?: string;
@@ -64,13 +66,10 @@ export const SearchInput = forwardRef<SearchInputProps, 'input'>(
6466
return () => clearTimeout(handler);
6567
}, [search, delay]);
6668

67-
useEffect(() => {
68-
if (externalValue !== searchRef.current) {
69-
// TODO @eslint-react rule
70-
// eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
71-
setSearch(externalValue);
72-
}
73-
}, [externalValue]);
69+
const externalValueHasChanged = useValueHasChanged(externalValue);
70+
if (externalValueHasChanged && externalValue !== searchRef.current) {
71+
setSearch(externalValue);
72+
}
7473

7574
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
7675
setSearch(event.target.value);

Diff for: src/hooks/useValueHasChanged.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useRef } from 'react';
2+
3+
export const useValueHasChanged = (value: unknown) => {
4+
const valueRef = useRef(value);
5+
const valueHasChanged = valueRef.current !== value;
6+
valueRef.current = value;
7+
8+
return valueHasChanged;
9+
};

0 commit comments

Comments
 (0)