Skip to content

Commit

Permalink
[Refactor] context provider를 컴포넌트로 분리 (#437)
Browse files Browse the repository at this point in the history
* refactor: device type provider component로 분리

* chore: alias 추가

* refactor: useDeviceType 적용

* refactor: recruiting info provider component로 분리

* refactor: useRecruitingInfo 적용

* refactor: theme provider component로 분리

* refactor: useTheme 적용

* docs: 기존 파일 제거

* chore: 에러 메세지 수정

* chore: 변수명 통일

* chore: 불필요한 export 제거
  • Loading branch information
eonseok-jeon authored Aug 28, 2024
1 parent 8577a94 commit b99be93
Show file tree
Hide file tree
Showing 64 changed files with 322 additions and 295 deletions.
45 changes: 11 additions & 34 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { add, init } from '@amplitude/analytics-browser';
import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser';
import { colors } from '@sopt-makers/colors';
import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { AxiosError } from 'axios';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';

import Layout from '@components/Layout';
import { useDevice } from '@hooks/useDevice';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { RecruitingInfoContext, RecruitingInfoType } from '@store/recruitingInfoContext';
import { ModeType, ThemeContext } from '@store/themeContext';
import DeviceTypeProvider from 'contexts/DeviceTypeProvider';
import RecruitingInfoProvider from 'contexts/RecruitingInfoProvider';
import ThemeProvider, { useTheme } from 'contexts/ThemeProvider';
import { dark, light } from 'styles/theme.css';
import { SessionExpiredDialog } from 'views/dialogs';
import ErrorPage from 'views/ErrorPage';
Expand Down Expand Up @@ -54,10 +52,8 @@ const App = () => {
// }, []);

const sessionRef = useRef<HTMLDialogElement>(null);

const [isLight, setIsLight] = useState(true);
const [recruitingInfo, setRecruitingInfo] = useState<RecruitingInfoType>({});
const [isAmplitudeInitialized, setIsAmplitudeInitialized] = useState(false);
const { isLight } = useTheme();

const queryClient = new QueryClient({
defaultOptions: {
Expand Down Expand Up @@ -93,25 +89,6 @@ const App = () => {
}),
});

const themeContextValue = {
isLight,
handleChangeMode: (mode: ModeType) => {
setIsLight(mode === 'light' ? true : false);
const body = document.body;
const bodyColor = mode === 'light' ? colors.white : colors.gray950; // theme.color.background
body.style.backgroundColor = bodyColor;
},
};

const recruitingInfoContextValue = {
recruitingInfo,
handleSaveRecruitingInfo: useCallback((obj: RecruitingInfoType) => {
setRecruitingInfo((prev) => ({ ...prev, ...obj }));
}, []),
};

const deviceType = useDevice();

useEffect(() => {
if (!isAmplitudeInitialized) {
init(import.meta.env.VITE_AMPLITUDE_API_KEY);
Expand All @@ -130,18 +107,18 @@ const App = () => {
return (
<>
<SessionExpiredDialog ref={sessionRef} />
<ThemeContext.Provider value={themeContextValue}>
<DeviceTypeContext.Provider value={{ deviceType }}>
<RecruitingInfoContext.Provider value={recruitingInfoContextValue}>
<ThemeProvider>
<DeviceTypeProvider>
<RecruitingInfoProvider>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<div className={isLight ? light : dark}>
<RouterProvider router={router} />
</div>
</QueryClientProvider>
</RecruitingInfoContext.Provider>
</DeviceTypeContext.Provider>
</ThemeContext.Provider>
</RecruitingInfoProvider>
</DeviceTypeProvider>
</ThemeProvider>
</>
);
};
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useContext, useId, type ButtonHTMLAttributes, type ReactNode } from 'react';
import { useId, type ButtonHTMLAttributes, type ReactNode } from 'react';
import { Link, To } from 'react-router-dom';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';
import ButtonLoading from 'views/loadings/ButtonLoading';

import { buttonFontVar, container, outsideBox, paddings } from './style.css';
Expand All @@ -26,7 +26,7 @@ const Button = ({
isLink = false,
...buttonElementProps
}: ButtonProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const { disabled, type = 'button' } = buttonElementProps;
const Tag = isLink ? Link : 'button';

Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Callout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { colors } from '@sopt-makers/colors';
import { IconAlertCircle } from '@sopt-makers/icons';
import { useContext, type HTMLAttributes, type ReactNode } from 'react';
import { type HTMLAttributes, type ReactNode } from 'react';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { buttonVar, container, warningWrapperVar } from './style.css';

Expand All @@ -13,7 +13,7 @@ interface CalloutProps extends HTMLAttributes<HTMLElement> {
}

const Callout = ({ children, size = 'sm', Button, ...calloutElementProps }: CalloutProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
return (
<article className={container[deviceType === 'DESK' ? size : deviceType]} {...calloutElementProps}>
<div className={warningWrapperVar[deviceType]}>
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forwardRef, useContext, type DialogHTMLAttributes, type ReactNode } from 'react';
import { forwardRef, type DialogHTMLAttributes, type ReactNode } from 'react';
import { createPortal } from 'react-dom';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { containerVar } from './style.css';

Expand All @@ -10,7 +10,7 @@ interface DialogProps extends DialogHTMLAttributes<HTMLDialogElement> {
}

const Dialog = forwardRef<HTMLDialogElement, DialogProps>(({ children, ...dialogElementProps }: DialogProps, ref) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

return createPortal(
<dialog ref={ref} className={containerVar[deviceType]} {...dialogElementProps}>
Expand Down
6 changes: 2 additions & 4 deletions src/common/components/Input/components/Description/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { useContext } from 'react';

import { TextBoxProps } from '@components/Input/types';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { descriptionFontVar, descriptionVar } from './style.css';

// TextBox 내부 Input 하단의 부가텍스트
const Description = ({ children, styleType = 'default' }: Pick<TextBoxProps, 'children' | 'styleType'>) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

return <div className={`${descriptionVar[styleType]} ${descriptionFontVar[deviceType]}`}>{children}</div>;
};
Expand Down
6 changes: 2 additions & 4 deletions src/common/components/Input/components/InputButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { useContext } from 'react';

import Button from '@components/Button';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { buttonVar } from './style.css';
import { InputButtonProps } from './types';

// TextBox 내부 InputLine 우측 버튼
const InputButton = ({ isLoading, text, ...props }: InputButtonProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
return (
<Button isLoading={isLoading} className={buttonVar[deviceType]} {...props}>
{text}
Expand Down
4 changes: 2 additions & 2 deletions src/common/components/Input/components/InputLine/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, useContext } from 'react';
import { useFormContext } from 'react-hook-form';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { inputFont, inputLineVar, inputVar } from './style.css';
import { formatBirthdate } from './utils/formatBirthdate';
Expand All @@ -27,7 +27,7 @@ const InputLine = ({
setValue,
} = useFormContext();
const { required } = useContext(FormContext);
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const { maxLength, minLength } = inputElementProps;
const { defaultValue } = inputElementProps;

Expand Down
8 changes: 4 additions & 4 deletions src/common/components/Input/components/InputTheme/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { track } from '@amplitude/analytics-browser';
import { useCallback, useContext, useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useFormContext } from 'react-hook-form';
import { useLocation, useNavigate } from 'react-router-dom';

Expand All @@ -9,7 +9,7 @@ import useMutateCheckUser from '@components/Input/hooks/useMutateCheckUser';
import useMutateSendCode from '@components/Input/hooks/useMutateSendCode';
import { VALIDATION_CHECK } from '@constants/validationCheck';
import useScrollToHash from '@hooks/useScrollToHash';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { successVar } from './style.css';
import InputLine from '../InputLine';
Expand Down Expand Up @@ -40,7 +40,7 @@ export const TextBox이메일 = ({
isVerified,
onChangeVerification,
}: TextBox이메일Props) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const location = useLocation();
const navigate = useNavigate();

Expand Down Expand Up @@ -188,7 +188,7 @@ export const TextBox이메일 = ({
};

export const TextBox비밀번호 = () => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

const location = useLocation();
const textVar = location.pathname === '/password' ? '새 비밀번호' : '비밀번호';
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Input/components/TextBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, useContext } from 'react';
import { createContext } from 'react';

import { TextBoxProps } from '@components/Input/types';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { circle, containerVar, titleVar } from './style.css';

Expand All @@ -15,7 +15,7 @@ export const TextBox = ({
size = 'md',
required,
}: Pick<TextBoxProps, 'children' | 'label' | 'name' | 'size' | 'required'>) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

return (
<FormContext.Provider value={{ required }}>
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Input/components/Timer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { differenceInSeconds } from 'date-fns';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { timerVar } from './style.css';
import { TimerProps } from './types';
Expand All @@ -11,7 +11,7 @@ const INITIAL_TIME = 300;

// TextBox 내부 타이머
const Timer = ({ isActive, onResetTimer }: TimerProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const [seconds, setSeconds] = useState(INITIAL_TIME - 1);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { track } from '@amplitude/analytics-browser';
import { useContext } from 'react';
import { NavLink } from 'react-router-dom';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import { menuItemVar, menuLinkVar } from './style.css';

Expand All @@ -16,7 +15,7 @@ interface MenuItemProps {
}

const MenuItem = ({ text, path, target, amplitudeId, className, onClick }: MenuItemProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

return (
<li className={`${className} ${menuItemVar[deviceType]}`}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { reset, track } from '@amplitude/analytics-browser';
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { RecruitingInfoContext } from '@store/recruitingInfoContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';
import { useRecruitingInfo } from 'contexts/RecruitingInfoProvider';

import { dimmedBgVar, menuContainerVar, menuList, menuMobListVar } from './style.css';
import { MENU_ITEMS, MENU_ITEMS_MAKERS } from '../../contants';
import MenuItem from '../MenuItem';

const MenuList = ({ isMenuOpen, onClickMenuToggle }: { isMenuOpen?: boolean; onClickMenuToggle?: () => void }) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const navigate = useNavigate();
const { pathname } = useLocation();
const [isShown, setIsShown] = useState(isMenuOpen);
Expand All @@ -31,7 +31,7 @@ const MenuList = ({ isMenuOpen, onClickMenuToggle }: { isMenuOpen?: boolean; onC

const {
recruitingInfo: { name, isMakers },
} = useContext(RecruitingInfoContext);
} = useRecruitingInfo();
const menuItems = isMakers ? MENU_ITEMS_MAKERS : MENU_ITEMS;
const handleLogout = () => {
track('click-gnb-logout');
Expand Down
9 changes: 4 additions & 5 deletions src/common/components/Layout/components/Header/Nav/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { IconMenu, IconXClose } from '@sopt-makers/icons';
import { useContext } from 'react';

import { DeviceTypeContext } from '@store/deviceTypeContext';
import { ThemeContext } from '@store/themeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';
import { useTheme } from 'contexts/ThemeProvider';
import { theme } from 'styles/theme.css';

import MenuList from './MenuList';
import { menuIconVar } from './style.css';

const Nav = ({ isMenuOpen, onClickMenuToggle }: { isMenuOpen: boolean; onClickMenuToggle: () => void }) => {
const { deviceType } = useContext(DeviceTypeContext);
const { isLight } = useContext(ThemeContext);
const { deviceType } = useDeviceType();
const { isLight } = useTheme();

return deviceType !== 'DESK' ? (
<i className={menuIconVar[deviceType]} onClick={onClickMenuToggle}>
Expand Down
14 changes: 7 additions & 7 deletions src/common/components/Layout/components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { useContext, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';

import MakersDarkLogo from '@assets/MakersDarkLogo';
import MakersLogo from '@assets/MakersLogo';
import NowsoptLogo from '@assets/NowsoptLogo';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { RecruitingInfoContext } from '@store/recruitingInfoContext';
import { ThemeContext } from '@store/themeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';
import { useRecruitingInfo } from 'contexts/RecruitingInfoProvider';
import { useTheme } from 'contexts/ThemeProvider';

import Nav from './Nav';
import MenuList from './Nav/MenuList';
import { containerSizeVer, containerVar, logoVar } from './style.css';

const Header = () => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();
const [isMenuOpen, setIsMenuOpen] = useState(false);
const handleClickMenuToggle = () => {
setIsMenuOpen((prev) => !prev);
Expand All @@ -24,8 +24,8 @@ const Header = () => {

const {
recruitingInfo: { isMakers },
} = useContext(RecruitingInfoContext);
const { isLight } = useContext(ThemeContext);
} = useRecruitingInfo();
const { isLight } = useTheme();

const handleClickLogo = () => {
pathname === '/' ? window.location.reload() : navigate('/');
Expand Down
6 changes: 3 additions & 3 deletions src/common/components/Select/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { IconChevronDown } from '@sopt-makers/icons';
import { ChangeEvent, useContext } from 'react';
import { ChangeEvent } from 'react';
import { useFormContext } from 'react-hook-form';

import { circle, containerVar, titleVar } from '@components/Input/components/TextBox/style.css';
import { DeviceTypeContext } from '@store/deviceTypeContext';
import { useDeviceType } from 'contexts/DeviceTypeProvider';

import {
errorVar,
Expand All @@ -17,7 +17,7 @@ import {
import { SelectBoxProps } from './type';

const SelectBox = ({ label, name, options, size = 'sm', required, ...inputElementProps }: SelectBoxProps) => {
const { deviceType } = useContext(DeviceTypeContext);
const { deviceType } = useDeviceType();

const { register, formState, clearErrors, getValues, setValue, setError } = useFormContext();
const { errors } = formState;
Expand Down
Loading

0 comments on commit b99be93

Please sign in to comment.