-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[11팀 이동아] [Chapter 1-3] React, Beyond the Basics #8
Open
Azamwa
wants to merge
19
commits into
hanghae-plus:main
Choose a base branch
from
Azamwa:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
b3e6625
shallowEquals 테스트 완성
Azamwa ee62819
깊은비교 테스트 완료
Azamwa 965dae1
shallowEquals 수정(useMemo 동작 버그)
Azamwa 27175fe
useRef, useMemo, useCallback 구현/테스트 완료
Azamwa 58f5476
코드정리(warn, 띄어쓰기)
Azamwa e79490f
코드정리(warn 제거, 띄어쓰기)
Azamwa ec70945
Merge branch 'main' of https://github.com/Azamwa/front_3rd_chapter1-3
Azamwa 56d4f6d
비교 - typeof === object이지만, null일 때 상황 추가
Azamwa 86c5c96
비교함수 리팩토링
Azamwa 4cf225f
비교함수 수정(참조비교로 값이 다를경우)
Azamwa 7713671
memo, deepMemo 구현. 기본테스트 완료
Azamwa 100b6df
APP.tsx => 컴포넌트 분리
Azamwa 9406806
useContextHook 생성(context null체크 관리 모듈)
Azamwa 130460d
각 ContextProvider 모듈화
Azamwa dac549c
userContext - notification 추가
Azamwa 0abf3a1
비교함수 유사배열 유무 추가
Azamwa 1540ed3
memo 수정
Azamwa ebeaecc
과제완료- contextProvider 위치 조정, 필요한부분 useMemo, memo 사용
Azamwa 6af89c5
react memo -> lib memo 변경
Azamwa File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/* eslint-disable react-refresh/only-export-components */ | ||
|
||
export * from "./notification-context"; | ||
export * from "./theme-context"; | ||
export * from "./user-context"; | ||
export * from "./useContextHook"; |
38 changes: 38 additions & 0 deletions
38
packages/assignment/src/@lib/context/notification-context.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { createContext, PropsWithChildren, useState } from "react"; | ||
|
||
interface Notification { | ||
id: number; | ||
message: string; | ||
type: "info" | "success" | "warning" | "error"; | ||
} | ||
|
||
export interface NotificationContextType { | ||
notifications: Notification[]; | ||
addNotification: (message: string, type: Notification["type"]) => void; | ||
removeNotification: (id: number) => void; | ||
} | ||
|
||
export const NotificationContext = createContext<NotificationContextType | null>(null); | ||
|
||
export const NotificationProvider: React.FC<PropsWithChildren> = ({ children }) => { | ||
const [notifications, setNotifications] = useState<Notification[]>([]); | ||
|
||
const addNotification = (message: string, type: Notification["type"]) => { | ||
const newNotification: Notification = { | ||
id: Date.now(), | ||
message, | ||
type, | ||
}; | ||
setNotifications((prev) => [...prev, newNotification]); | ||
}; | ||
|
||
const removeNotification = (id: number) => { | ||
setNotifications((prev) => prev.filter((notification) => notification.id !== id)); | ||
}; | ||
|
||
return ( | ||
<NotificationContext.Provider value={{ notifications, addNotification, removeNotification }}> | ||
{children} | ||
</NotificationContext.Provider> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { createContext, PropsWithChildren, useState } from "react"; | ||
|
||
export interface ThemeContextType { | ||
theme: "light" | "dark"; | ||
toggleTheme: () => void; | ||
} | ||
|
||
export const ThemeContext = createContext<ThemeContextType | null>(null); | ||
|
||
export const ThemeProvider: React.FC<PropsWithChildren> = ({ children }) => { | ||
const [theme, setTheme] = useState<ThemeContextType["theme"]>("light"); | ||
const toggleTheme = () => { | ||
setTheme((state) => (state === "light" ? "dark" : "light")); | ||
}; | ||
|
||
return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { useContext } from "react"; | ||
|
||
interface UseContextHookProps<T> { | ||
context: React.Context<T | null>; | ||
name: string; | ||
} | ||
|
||
// context선언 후 null 체크 | ||
export const useContextHook = <T,>({ context, name }: UseContextHookProps<T>) => { | ||
const result = useContext(context); | ||
if (result === null) { | ||
throw new Error(`use${name} must be used within a ${name}Provider`); | ||
} | ||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { createContext, PropsWithChildren, useState } from "react"; | ||
import { useContextHook } from "./useContextHook"; | ||
import { NotificationContext } from "./notification-context"; | ||
import { useMemo } from "../hooks"; | ||
|
||
export interface UserType { | ||
id: number; | ||
name: string; | ||
email: string; | ||
} | ||
|
||
export interface UserContextType { | ||
user: UserType | null; | ||
login: (email: string, password: string) => void; | ||
logout: () => void; | ||
} | ||
|
||
export const UserContext = createContext<UserContextType | null>(null); | ||
|
||
export const UserProvider: React.FC<PropsWithChildren> = ({ children }) => { | ||
const [user, setUser] = useState<UserContextType["user"] | null>(null); | ||
const { addNotification } = useContextHook({ | ||
context: NotificationContext, | ||
name: "Notification", | ||
}); | ||
|
||
const login = (email: string) => { | ||
setUser({ id: 1, name: "홍길동", email }); | ||
addNotification("성공적으로 로그인되었습니다", "success"); | ||
}; | ||
|
||
const logout = () => { | ||
setUser(null); | ||
addNotification("로그아웃되었습니다", "info"); | ||
}; | ||
|
||
const userContextMemo = useMemo(() => { | ||
return { user, login, logout }; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [user]); | ||
|
||
return <UserContext.Provider value={userContextMemo}>{children}</UserContext.Provider>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,38 @@ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function deepEquals(objA: any, objB: any): boolean { | ||
return objA === objB; | ||
if (objA === objB) { | ||
return true; | ||
} | ||
|
||
if (objA == null || objB == null) { | ||
return false; | ||
} | ||
|
||
if (typeof objA !== "object" || typeof objB !== "object") { | ||
return objA === objB; | ||
} | ||
|
||
// 유사배열 유무 비교 | ||
if (Array.isArray(objA) !== Array.isArray(objB)) { | ||
return false; | ||
} | ||
|
||
const keysA = Object.keys(objA); | ||
const keysB = Object.keys(objB); | ||
|
||
if (keysA.length !== keysB.length) { | ||
return false; | ||
} | ||
|
||
for (const key of keysA) { | ||
if (!keysB.includes(key)) { | ||
return false; | ||
} | ||
|
||
if (!deepEquals(objA[key], objB[key])) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,36 @@ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function shallowEquals(objA: any, objB: any): boolean { | ||
return objA === objB; | ||
if (objA === objB) { | ||
return true; | ||
} | ||
if (objA === null || objB === null) { | ||
return false; | ||
} | ||
|
||
if (typeof objA !== "object" || typeof objB !== "object") { | ||
return objA === objB; | ||
} | ||
|
||
// 유사배열 유무 비교 | ||
if (Array.isArray(objA) !== Array.isArray(objB)) { | ||
return false; | ||
} | ||
|
||
const keysA = Object.keys(objA); | ||
const keysB = Object.keys(objB); | ||
|
||
if (keysA.length !== keysB.length) { | ||
return false; | ||
} | ||
|
||
for (const key of keysA) { | ||
if (!keysB.includes(key)) { | ||
return false; | ||
} | ||
if (objA[key] !== objB[key]) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { deepEquals } from "../equalities"; | ||
import { ComponentType, memo } from "react"; | ||
|
||
export function deepMemo<P extends object>(Component: ComponentType<P>) { | ||
return memo(Component, deepEquals); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { ComponentType, createElement } from "react"; | ||
import { shallowEquals } from "../equalities"; | ||
import { useMemo, useRef } from "../hooks"; | ||
|
||
export function memo<P extends object>(Component: ComponentType<P>, equals = shallowEquals) { | ||
return (props: P) => { | ||
const oldProps = useRef<P | null>(null); | ||
|
||
if (!equals(props, oldProps.current)) { | ||
oldProps.current = props; | ||
} | ||
|
||
const MemoizedComponent = useMemo(() => { | ||
return createElement(Component, oldProps.current); | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [oldProps.current]); | ||
|
||
return MemoizedComponent; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,13 @@ | ||
import { DependencyList } from "react"; | ||
import { useMemo } from "./useMemo"; | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any | ||
export function useCallback<T extends (...args: any[]) => any>(factory: T, deps: DependencyList): T { | ||
// 직접 작성한 useMemo를 통해서 만들어보세요. | ||
return ((...args) => factory(...args)) as T | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function useCallback<T extends (...args: any[]) => any>( | ||
factory: T, | ||
deps: DependencyList | ||
): T { | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
return useMemo(() => factory, deps); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,22 @@ | ||
import { DependencyList } from "react"; | ||
import { useRef } from "./useRef"; | ||
import { shallowEquals } from "../equalities"; | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
export function useMemo<T>(factory: () => T, deps: DependencyList, equals = shallowEquals): T { | ||
// 직접 작성한 useRef를 통해서 만들어보세요. | ||
return factory(); | ||
const oldDeps = useRef<Readonly<DependencyList>>(deps); | ||
const memoized = useRef<T | null>(null); | ||
|
||
if (memoized.current === null) { | ||
memoized.current = factory(); | ||
} | ||
|
||
if (memoized.current !== null && !equals(deps, oldDeps.current)) { | ||
memoized.current = factory(); | ||
} | ||
|
||
oldDeps.current = deps; | ||
|
||
return memoized.current; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
import { useState } from "react"; | ||
|
||
export function useRef<T>(initialValue: T): { current: T } { | ||
// React의 useState를 이용해서 만들어보세요. | ||
return { current: initialValue }; | ||
const [ref] = useState<{ current: T }>({ current: initialValue }); | ||
|
||
return ref; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일주일 동안 고민하면서 작성하신게 보이네요ㅠㅠ 고생하셨습니다
작성하신 코드 잘 보고갑니다!!!