From f25a68d513859c88afa4bbb7af01f7e03039600a Mon Sep 17 00:00:00 2001 From: Jaewook Ahn Date: Sat, 7 May 2022 18:56:09 +0900 Subject: [PATCH] Refactor code --- src/App.tsx | 73 +++++++++++++----------- src/components/AddressList/index.tsx | 11 +++- src/components/ClickToCopyText/index.tsx | 22 ++++--- src/components/FeedbackPopover.tsx | 20 ++++--- src/index.tsx | 9 ++- 5 files changed, 85 insertions(+), 50 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 94e4442..76cde14 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,10 @@ /* eslint-disable jsx-a11y/accessible-emoji */ -import React from "react"; +/** + * External modules + */ +import React, { useCallback, useEffect, useMemo, useState } from "react"; import styled from "styled-components"; import "antd/dist/antd.css"; -import "./App.css"; import { Button, Input, @@ -17,10 +19,15 @@ import { LoadingOutlined, ReloadOutlined, } from "@ant-design/icons"; + +/** + * Internal modules + */ +import "./App.css"; import { AddressData, AddressManager } from "./AddressManager"; import { AddressList } from "./components/AddressList"; -import { getRuntime } from "./utils"; import { SettingsManager, Settings } from "./SettingsManager"; +import { getRuntime } from "./utils"; const Header = styled.div` position: fixed; @@ -92,7 +99,7 @@ const ListEnd = styled.div` } `; -const Spinner: React.FC = () => ( +const Spinner = () => ( } /> ); @@ -103,25 +110,25 @@ interface Props { export const App = (props: Props) => { const { settings, address } = props; - const [searchValue, setSearchValue] = React.useState(""); - const [showLoading, setShowLoading] = React.useState(false); - const [isEnd, setIsEnd] = React.useState(false); - const [showEngAddr, setShowEngAddr] = React.useState(true); - const [showRoadAddr, setShowRoadAddr] = React.useState(true); - const [showLegacyAddr, setShowLegacyAddr] = React.useState(true); - const [addressData, setAddressData] = React.useState([]); - const [updatingSettings, setUpdatingSettings] = React.useState(false); + const [searchValue, setSearchValue] = useState(""); + const [loading, setLoading] = useState(false); + const [isEnd, setIsEnd] = useState(false); + const [showEngAddr, setShowEngAddr] = useState(true); + const [showRoadAddr, setShowRoadAddr] = useState(true); + const [showLegacyAddr, setShowLegacyAddr] = useState(true); + const [addressData, setAddressData] = useState([]); + const [updatingSettings, setUpdatingSettings] = useState(false); - const handleSearchValueChange = React.useCallback((e: React.ChangeEvent) => { + const handleSearchValueChange = useCallback((e: React.ChangeEvent) => { setSearchValue(e.target.value); }, [setSearchValue]); - const handleSearchClick = React.useCallback(() => { + const handleSearchClick = useCallback(() => { if (address.previousSearchKey?.keyword === searchValue) { return; } - setShowLoading(true); + setLoading(true); setAddressData([]); window.ga("send", "event", "address", "search", searchValue); @@ -135,17 +142,17 @@ export const App = (props: Props) => { }).catch((err) => { console.error(err); }).finally(() => { - setShowLoading(false); + setLoading(false); }); - }, [address, searchValue, setAddressData, setShowLoading]); + }, [address, searchValue]); - const handleResetClick = React.useCallback(() => { + const handleResetClick = useCallback(() => { setSearchValue(""); handleSearchClick(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setSearchValue]); + }, []); - const handleSearchOptionClick = React.useCallback((type: "eng" | "road" | "legacy") => async () => { + const handleSearchOptionClick = useCallback((type: "eng" | "road" | "legacy") => async () => { if (updatingSettings) { return; } @@ -178,10 +185,10 @@ export const App = (props: Props) => { } finally { setUpdatingSettings(false); } - }, [settings, showEngAddr, showRoadAddr, showLegacyAddr, updatingSettings, setShowEngAddr, setShowRoadAddr, setShowLegacyAddr, setUpdatingSettings]); + }, [settings, showEngAddr, showRoadAddr, showLegacyAddr, updatingSettings]); - const loadNextAddress = React.useCallback(() => { - if (showLoading) { + const loadNextAddress = useCallback(() => { + if (loading) { return; } if (address.previousSearchKey?.end) { @@ -190,7 +197,7 @@ export const App = (props: Props) => { return; } - setShowLoading(true); + setLoading(true); const nextPage = (Number.parseInt(address.previousSearchKey?.currentPage!) + 1).toString(); window.ga("send", "event", "address", "search", "추가 데이터 로드", Number.parseInt(nextPage)); @@ -202,11 +209,11 @@ export const App = (props: Props) => { }).catch((err) => { console.error(err); }).finally(() => { - setShowLoading(false); + setLoading(false); }); - }, [address, showLoading, setShowLoading]); + }, [address, loading]); - const Options = React.useMemo(() => [ + const Options = useMemo(() => [ , ], [showEngAddr, showRoadAddr, showLegacyAddr, updatingSettings, handleSearchOptionClick]); - const handleScrollEvent = React.useCallback(() => { + const handleScrollEvent = useCallback(() => { const list = document.getElementsByClassName("address-list"); const bottom = list[0]?.getBoundingClientRect().bottom ?? 0; if (bottom > 0 && bottom <= window.innerHeight) { @@ -230,11 +237,11 @@ export const App = (props: Props) => { } }, [loadNextAddress]); - React.useEffect(() => { + useEffect(() => { if (getRuntime() === "extension") { settings?.once("ready", () => { console.log("Settings loaded", settings); - const searchResult = settings?.settings?.searchResult; + const searchResult = settings.settings?.searchResult; setShowEngAddr(searchResult?.showEng ?? false); setShowRoadAddr(searchResult?.showRoad ?? true); setShowLegacyAddr(searchResult?.showLegacy ?? true); @@ -247,7 +254,7 @@ export const App = (props: Props) => { } }, [settings]); - React.useEffect(() => { + useEffect(() => { document.addEventListener("scroll", handleScrollEvent); return () => { document.removeEventListener("scroll", handleScrollEvent); @@ -263,7 +270,7 @@ export const App = (props: Props) => { enterButton allowClear placeholder="검색할 주소 입력" value={searchValue} - loading={showLoading} + loading={loading} onChange={handleSearchValueChange} onSearch={handleSearchClick} /> @@ -275,7 +282,7 @@ export const App = (props: Props) => { showEngAddr={showEngAddr} showRoadAddr={showRoadAddr} showLegacyAddr={showLegacyAddr} /> - {showLoading ? ( + {loading ? ( ) : isEnd ? ( diff --git a/src/components/AddressList/index.tsx b/src/components/AddressList/index.tsx index 630c916..9f8acba 100644 --- a/src/components/AddressList/index.tsx +++ b/src/components/AddressList/index.tsx @@ -1,11 +1,18 @@ /* eslint-disable jsx-a11y/accessible-emoji */ +/** + * External modules + */ import React from "react"; import styled from "styled-components"; import { Collapse, Typography } from "antd"; + +/** + * Internal modules + */ +import "./AddressList.css"; import { AddressData } from "../../AddressManager"; import { ClickToCopyText } from "../ClickToCopyText"; import { FeedbackPopover } from "../FeedbackPopover"; -import "./AddressList.css"; interface Props { data: AddressData[]; @@ -27,6 +34,7 @@ const EmptyText = styled(Typography.Paragraph)` export const AddressList = (props: Props) => { const { data, showEngAddr, showRoadAddr, showLegacyAddr } = props; + if (!data || !data.length) { return 검색 결과가 없습니다.
@@ -34,6 +42,7 @@ export const AddressList = (props: Props) => {
; } + return ( {data.map((row, i) => ( diff --git a/src/components/ClickToCopyText/index.tsx b/src/components/ClickToCopyText/index.tsx index 935b2ac..2e37efb 100644 --- a/src/components/ClickToCopyText/index.tsx +++ b/src/components/ClickToCopyText/index.tsx @@ -1,9 +1,16 @@ -import React from "react"; +/** + * External modules + */ +import React, { useCallback, useState } from "react"; import { Button, Tooltip, } from "antd"; import copy from "copy-to-clipboard"; + +/** + * Internal modules + */ import "./ClickToCopyText.css"; interface ClickToCopyTextProps { @@ -11,20 +18,21 @@ interface ClickToCopyTextProps { analytics?: "우편번호" | "도로명주소" | "지번주소" | "영문주소"; } -export const ClickToCopyText: React.FC = ({ children, analytics }: ClickToCopyTextProps) => { - const [copied, setCopied] = React.useState(false); +export const ClickToCopyText = (props: ClickToCopyTextProps) => { + const { children, analytics } = props; + const [copied, setCopied] = useState(false); - const handleCopyClick = React.useCallback(() => { + const handleCopyClick = useCallback(() => { setCopied(true); copy(children); window.ga("send", "event", "address", "copy", `${analytics} 복사`); - }, [setCopied, analytics, children]); + }, [analytics, children]); - const handleVisibleChange = React.useCallback((visible: boolean) => { + const handleVisibleChange = useCallback((visible: boolean) => { if (!visible) { setTimeout(() => setCopied(false), 100); } - }, [setCopied]); + }, []); return ( diff --git a/src/components/FeedbackPopover.tsx b/src/components/FeedbackPopover.tsx index b368a1e..0db2c14 100644 --- a/src/components/FeedbackPopover.tsx +++ b/src/components/FeedbackPopover.tsx @@ -1,5 +1,8 @@ /* eslint-disable jsx-a11y/accessible-emoji */ -import React from "react"; +/** + * External modules + */ +import React, { useCallback, useState } from "react"; import styled from "styled-components"; import { Button, Input, Popover, Typography } from "antd"; import axios from "axios"; @@ -31,12 +34,12 @@ const PopoverContentStyle = { }; const FeedbackForm = () => { - const [message, setMessage] = React.useState(""); - const [sending, setSending] = React.useState(false); - const [sent, setSent] = React.useState(false); + const [message, setMessage] = useState(""); + const [sending, setSending] = useState(false); + const [sent, setSent] = useState(false); - const handleSendClick = React.useCallback(async () => { - if (sent) { + const handleSendClick = useCallback(async () => { + if (sent || !message) { return; } setSending(true); @@ -48,14 +51,15 @@ const FeedbackForm = () => { } finally { setSending(false); } - }, [message, sent, setSent, setSending]); + }, [message, sent]); + return setMessage(ev.target.value)} /> - ; diff --git a/src/index.tsx b/src/index.tsx index d28800a..f2573cf 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,10 +1,17 @@ +/** + * External modules + */ import React from "react"; import * as Sentry from "@sentry/react"; import { render } from "react-dom"; + +/** + * Internal modules + */ import { App } from "./App"; -import { getRuntime, isProduction } from "./utils"; import { AddressManager } from "./AddressManager"; import { SettingsManager, Settings, DEFAULT_SETTINGS } from "./SettingsManager"; +import { getRuntime, isProduction } from "./utils"; // inject NODE_ENV variable into window object window.__ENV__ = {