Skip to content

Commit

Permalink
Apply react hook form (#18)
Browse files Browse the repository at this point in the history
* react-hook-form 설치

* ControlledLiveToggle 컴포넌트 추가

* react hook form 기반으로 Form 컴포넌트 추가

* debounce util 추가

* Form 컴포넌트 기반으로 페이지 로직 변경

* Form에 value type 지정
  • Loading branch information
hmu332233 authored Nov 20, 2022
1 parent 27b17d3 commit 2fab17d
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 101 deletions.
28 changes: 28 additions & 0 deletions components/ControlledLiveToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useEffect } from 'react';

import useToggle from 'hooks/useToggle';

import LiveToggle from 'components/LiveToggle';
import { useFormContext } from 'react-hook-form';

type Props = {
name: string;
};

function ControlledLiveToggle({ name }: Props) {
const [isLiveMode, toggleIsLiveMode] = useToggle(true);

const { setValue, register } = useFormContext();

useEffect(() => {
register(name);
}, [name, register]);

useEffect(() => {
setValue(name, isLiveMode);
}, [name, isLiveMode, setValue]);

return <LiveToggle value={isLiveMode} onClick={toggleIsLiveMode} />;
}

export default ControlledLiveToggle;
108 changes: 108 additions & 0 deletions components/MoonForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useEffect } from 'react';

import { debounce } from 'utils';
import { objectToQueryString } from 'utils/string';

import { FormProvider, useForm } from 'react-hook-form';
import ControlledLiveToggle from 'components/ControlledLiveToggle';

type FormValues = {
liveMode: boolean;
dateString: string;
size: string;
theme: string;
rotate: string;
};

type Props = {
onChange: (v: string) => void;
};

function MoonForm({ onChange }: Props) {
const formMethods = useForm<FormValues>({
defaultValues: {
liveMode: true,
dateString: '',
size: '',
theme: 'basic',
rotate: '0',
},
});

const { register, watch } = formMethods;

const liveMode = watch('liveMode');

useEffect(() => {
const subscription = watch(
debounce(({ liveMode, dateString, size, theme, rotate }: FormValues) => {
const queryString = objectToQueryString({
liveMode,
date: liveMode ? '' : dateString,
size,
theme,
rotate,
});

onChange(queryString);
}, 100),
);
return () => subscription.unsubscribe();
}, [watch, onChange]);

return (
<FormProvider {...formMethods}>
<ControlledLiveToggle name="liveMode" />
{!liveMode && (
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Date</span>
</label>
<input
type="date"
className="input input-bordered w-full max-w-xs"
{...register('dateString')}
/>
</div>
)}
<div className="form-control w-full max-w-xs">
<label className="label" htmlFor="theme">
<span className="label-text">Theme</span>
</label>
<select
id="theme"
className="select input-bordered w-full max-w-xs"
{...register('theme')}
>
<option value="basic">Basic</option>
<option value="ray">Ray</option>
</select>
</div>
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Rotate</span>
</label>
<input
type="range"
min="0"
max="360"
className="range"
{...register('rotate')}
/>
</div>
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Size</span>
</label>
<input
type="number"
placeholder="100 (default)"
className="input input-bordered w-full max-w-xs"
{...register('size')}
/>
</div>
</FormProvider>
);
}

export default MoonForm;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"classnames": "^2.3.2",
"next": "12.3.0",
"react": "18.2.0",
"react-dom": "18.2.0"
"react-dom": "18.2.0",
"react-hook-form": "^7.39.4"
},
"devDependencies": {
"@types/node": "18.7.18",
Expand Down
107 changes: 7 additions & 100 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,18 @@ import CopyModal from 'components/CopyModal';
import Hits from 'components/Hits';

import useDebounce from 'hooks/useDebounce';
import useToggle from 'hooks/useToggle';

import { objectToQueryString } from 'utils/string';
import LiveToggle from 'components/LiveToggle';

import Adfit from 'components/Adfit';
import OgTags from 'components/OgTags';

function Home() {
const [isLiveMode, toggleIsLiveMode] = useToggle(true);
const [dateString, setDateString] = useState('');
const [size, setSize] = useState('');
const [theme, setTheme] = useState('basic');
const [rotate, setRotate] = useState('0');

const queryString = useDebounce(
objectToQueryString({
date: isLiveMode ? '' : dateString,
size,
theme,
rotate,
}),
300,
);

const handleDateStringChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setDateString(value);
};
import { FormProvider, useForm } from 'react-hook-form';
import ControlledLiveToggle from 'components/ControlledLiveToggle';
import MoonForm from 'components/MoonForm';

const handleSizeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setSize(value);
};

const handleRotateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {
currentTarget: { value },
} = e;
setRotate(value);
};

const handleThemeChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const {
currentTarget: { value },
} = e;

setTheme(value);
};
function Home() {
const [queryString, setQueryString] = useState('');

const svgUrl = `/moon.svg${queryString}`;

Expand All @@ -69,60 +29,7 @@ function Home() {
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={svgUrl} alt="moon.svg" />
</a>
<LiveToggle value={isLiveMode} onClick={toggleIsLiveMode} />
{!isLiveMode && (
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Date</span>
</label>
<input
type="date"
className="input input-bordered w-full max-w-xs"
value={dateString}
onChange={handleDateStringChange}
/>
</div>
)}
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Theme</span>
</label>
<select
id="theme"
name="theme"
className="select input-bordered w-full max-w-xs"
value={theme}
onChange={handleThemeChange}
>
<option value="basic">Basic</option>
<option value="ray">Ray</option>
</select>
</div>
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Rotate</span>
</label>
<input
type="range"
min="0"
max="360"
value={rotate}
onChange={handleRotateChange}
className="range"
/>
</div>
<div className="form-control w-full max-w-xs">
<label className="label">
<span className="label-text">Size</span>
</label>
<input
type="number"
placeholder="100 (default)"
className="input input-bordered w-full max-w-xs"
value={size}
onChange={handleSizeChange}
/>
</div>
<MoonForm onChange={setQueryString} />
<CopyModal.Button id="copy-modal" />
<CopyModal.Modal
id="copy-modal"
Expand Down
7 changes: 7 additions & 0 deletions utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const debounce = (fn: Function, ms = 100) => {
let timeoutId: ReturnType<typeof setTimeout>;
return function (this: any, ...args: any[]) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2339,6 +2339,11 @@ react-dom@18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"

react-hook-form@^7.39.4:
version "7.39.4"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.39.4.tgz#7d9edf4e778a0cec4383f0119cd0699e3826a14a"
integrity sha512-B0e78r9kR9L2M4A4AXGbHoA/vyv34sB/n8QWJAw33TFz8f5t9helBbYAeqnbvcQf1EYzJxKX/bGQQh9K+evCyQ==

react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
Expand Down

1 comment on commit 2fab17d

@vercel
Copy link

@vercel vercel bot commented on 2fab17d Nov 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.