{"id":26682647,"url":"https://github.com/seonhyungjo/react-hook-sample","last_synced_at":"2025-09-13T14:07:48.552Z","repository":{"id":39551611,"uuid":"170611418","full_name":"SeonHyungJo/react-hook-sample","owner":"SeonHyungJo","description":"This is place to study react-hook. Let's create custom hooks","archived":false,"fork":false,"pushed_at":"2023-01-06T01:47:22.000Z","size":2104,"stargazers_count":4,"open_issues_count":24,"forks_count":4,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-26T08:34:24.063Z","etag":null,"topics":["practise","react","react-dom","react-hooks","webpack-dev-server"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SeonHyungJo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-14T02:11:10.000Z","updated_at":"2023-08-22T07:09:47.000Z","dependencies_parsed_at":"2023-02-05T02:01:25.718Z","dependency_job_id":null,"html_url":"https://github.com/SeonHyungJo/react-hook-sample","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeonHyungJo%2Freact-hook-sample","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeonHyungJo%2Freact-hook-sample/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeonHyungJo%2Freact-hook-sample/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeonHyungJo%2Freact-hook-sample/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SeonHyungJo","download_url":"https://codeload.github.com/SeonHyungJo/react-hook-sample/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248575738,"owners_count":21127264,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["practise","react","react-dom","react-hooks","webpack-dev-server"],"created_at":"2025-03-26T08:27:44.447Z","updated_at":"2025-04-12T13:51:36.688Z","avatar_url":"https://github.com/SeonHyungJo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-hook-sample\n\n## Contents\n\n- [x] [useState](#1.-useState)\n- [x] [useEffect](#2.-useEffect)\n- [x] [useContext](#3.-useContext)\n- [x] [useReducer](#4.-useReducer)\n- [x] [useCallback](#5.-useCallback)\n- [x] [useMemo](#6.-useMemo)\n- [x] [useRef](#7.-useRef)\n- [x] [useImperativeHandle](#8.-useImperativeHandle)\n- [x] [useLayoutEffect](#9.-useLayoutEffect)\n- [x] [useDebugValue](#10.-useDebugValue)\n\n\n## Basic Hooks\n\n### **1. useState**\n\n```js\nconst [state, setState] = useState(initialState);\n```\n\n첫번째 인자로 **초기값** 을 넣어준다. `useState` 는 우리가 많이 사용하던 `state` 초기화 작업과 동일하다고 생각하면 쉽다. 생성시 우리는 배열을 받아 올 수 있다.\n\n**모든 Hook은 2개의 인자를 배열로 받아온다.(드물게 아닌 것도 있다.)**\n\n배열의 첫번째는 우리가 넘겨주었던 초기화 값이 들어오고 이후에는 변경된 값이 넘어오게 된다. 두번째는 값를 변경하는 함수다.\n\n**re-render가 될 경우 useState에 의해 반환된 첫 번째 값은 항상 업데이트를 적용한 후 가장 최근 상태가 된다.**\n\n\u003cbr/\u003e\n\n#### 1.1 Functional updates\n\n역시나 우리는 인자로 함수를 보낼수 있다. 그러나 기존에 우리가 사용하던 `setState` 메소드와는 달라서 자동으로 병합을 시켜주지 않는다. \n\n**즉 우리가 만들때 마다 필요에 따라 병합을 해줘야한다.**\n\n```js\nsetState(prevState =\u003e {\n  // Object.assign would also work\n  return {...prevState, ...updatedValues}; // 간단하게 spread를 사용해서 새로 생성\n});\n```\n\n\u003cbr/\u003e\n\n#### 1.2 Lazy initial state\n\n`initial state` 에 역시 함수를 사용할 수 있다. 리액트 사이트에서 표현으로는 **값 비싼 계산** 이라면 함수를 사용해서 표현하라고 한다.\n\n```js\nconst [state, setState] = useState(() =\u003e {\n  const initialState = someExpensiveComputation(props);\n  return initialState;\n});\n```\n\n\u003cbr/\u003e\n\n#### 1.3 Bailing out of a state update\n\n같은 값을 다시 업데이트를 하더라도 리액트에서는 자식을 `re-render` 하거나 `effect` 를 발생시키지 않는다.([Object.is](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is)를 사용해서 비교했다고 함_읽으면 유용함) \n\n\u003cbr/\u003e\n\n### **2. useEffect()**\n\n```js\nuseEffect(() =\u003e {\n  // Update the document title using the browser API\n  document.title = `You clicked ${count} times`;\n});\n```\n\n기본적으로 모든 렌더링이 완료된 후에 실행이 된다. \n\n어떤 `value` 가 바뀌었을때 실행되도록 할 수 있다.(내가 바라보고 싶은 인자를 넣을 수 있다.)\n\n\u003cbr/\u003e\n\n#### 2.1 Cleaning up an effect\n\n화면을 떠나기 전에 정리를 할때 사용할 수 있다.(`dispose()`와 같은 느낌임)\n\n아래의 예시를 보게 되면 `return`으로 만든 함수가 정리를 하는 함수이다.\n\n```js\nuseEffect(() =\u003e {\n  const subscription = props.source.subscribe();\n  return () =\u003e {\n    // Clean up the subscription\n    subscription.unsubscribe();\n  };\n});\n```\n\n메모리 부족을 방지하하기 위해 컴포넌트가 UI로 부터 제거되기 전에 실행한다.\n\n\u003e 만약에 구독을 취소하기전에 다른 effect가 실행이 되어 구독을 한다면? 이러한 일이 발생하는 것을 막고 싶다면?\n\n\u003cbr/\u003e\n\n#### 2.2 Timing of effects\n\n`componentDidMount` 나 `componentDidUpdate` 과는 다르게 `layout`과 `paint` 가 끝나고 나서 `useEffect`가 실행이된다. 이렇게 되니 만든 **공통의 부작용에 있어서 적합하다** 라고 하는데, 화면에서 업데이트되는 것이 멈추는 현상이 일어나지 않으니까 그런 것이다.\n\n예를 들어, **사용자가 볼 수있는 DOM 변이는 시각적인 불일치를 인식하지 못하도록 다음 페인트 전에 동기적으로 실행해야 한다.**\n\n`useLayoutEffect` 는 `useEffect` 동일한 기능이지만 실행되는 시점이 다른 `Hook` 이다. 쉽게 말함면 실행되는 시점이 다르다는 것인데 `useEffect` 는 아래에 진한 글씨와 같이 완전히 화면이 그려졌다 라는 것을 보장할 수 있지만 `useLayoutEffect`는 시점이 Dom의 변화는 있지만 `repaint`, `reflow`가 되지 않은 시점이라고 생각하면 될 것 같다.\n\n**`useEffect`는 브라우저가 paint할때 까지 연기를 하지만 새로운 렌더링전에 시작될 것이 보증한다.**\n\n\u003cbr/\u003e\n\n#### 2.3 Conditionally firing an effect\n\n```js\nuseEffect(\n  () =\u003e {\n    const subscription = props.source.subscribe();\n    return () =\u003e {\n      subscription.unsubscribe();\n    };\n  },\n  [props.source],\n);\n```\n\n`useEffect` 에는 2번째 인자가 존재한다. 여기에 들어가는 배열은 **내가 바라볼 것** 으로 여기서는 `props.source` 의 변경이 일어났을 경우에만 작동하게 된다.\n\n**만약에 인자에 아무것도 없다면 마운트에서 실행되고 마운트 해제에서는 정리한다.**\n\n\u003cbr/\u003e\n\n### **3. useContext()**\n\n```js\n    const context = useContext(Context);\n```\n\n`react@16.3`부터 지원을 한 `Context` 를 사용할 수 있도록 지원하는 것이다. `context object` 를 받아서 현재 `context` 값을 반환한다. \n\n`provider`가 업데이트되면 Hooks이 실행되어 최신의 값으로 당연히 동기화가 된다.\n\n\u003cbr/\u003e\n\n## Additional Hooks\n\n### **4. useReducer**\n\n우리가 리덕스에서 많이 보던 `Reducer` 와 비슷하다고 생각하면 된다. 그렇다고 한다.\n\n```js\n    const initialState = {count: 0};\n    \n    // 우리가 Redux에서 만들던 Reducer 부분\n    function reducer(state, action) {\n      switch (action.type) {\n        case 'increment':\n          return {count: state.count + 1};\n        case 'decrement':\n          return {count: state.count - 1};\n        default:\n          throw new Error();\n      }\n    }\n    \n    function Counter({initialCount}) {\n      // 초기화 값과 Reducer를 넣어준다.\n      const [state, dispatch] = useReducer(reducer, initialState);\n\n      return (\n        \u003c\u003e\n          Count: {state.count}\n          \u003cbutton onClick={() =\u003e dispatch({type: 'increment'})}\u003e+\u003c/button\u003e\n          \u003cbutton onClick={() =\u003e dispatch({type: 'decrement'})}\u003e-\u003c/button\u003e\n        \u003c/\u003e\n      );\n    }\n```\n\n\u003cbr/\u003e\n\n#### 4.1 Specifying the initial state\n\n2번째 인자로 초기화 `state` 를 넘겨준다.(간단하게 초기화하는 방법)\n\n```js\n    const [state, dispatch] = useReducer(\n        reducer,\n        {count: initialCount}\n      );\n```\n\n\u003cbr/\u003e\n\n#### 4.2 Lazy initialization\n\n만약 느슨하게 초기 `state` 를 만들고 싶다면 3번째 인자로 `init function` 을 넘기자\n\n그러면 `init(initialArg)` 이런식으로 호출이 될 것이다.\n\n```js\n    function init(initialCount) {\n      return {count: initialCount};\n    }\n    \n    function reducer(state, action) {\n      switch (action.type) {\n        case 'increment':\n          return {count: state.count + 1};\n        case 'decrement':\n          return {count: state.count - 1};\n        case 'reset':\n          return init(action.payload);\n        default:\n          throw new Error();\n      }\n    }\n    \n    function Counter({initialCount}) {\n      const [state, dispatch] = useReducer(reducer, initialCount, init);\n      return (\n        \u003c\u003e\n          Count: {state.count}\n          \u003cbutton\n            onClick={() =\u003e dispatch({type: 'reset', payload: initialCount})}\u003e\n    \n            Reset\n          \u003c/button\u003e\n          \u003cbutton onClick={() =\u003e dispatch({type: 'increment'})}\u003e+\u003c/button\u003e\n          \u003cbutton onClick={() =\u003e dispatch({type: 'decrement'})}\u003e-\u003c/button\u003e\n        \u003c/\u003e\n      );\n    }\n```\n\n\u003cbr/\u003e\n\n#### 4.3 Bailing out of a dispatch\n\n`useState` 와 동일하게 같은 값을 넘기게 되면 내부적으로 비교를 해서 자식 렌더링과 `effect`를 실행하는 것을 막는다.\n\n\u003cbr/\u003e\n\n### **5. useCallback**\n\n```js\n    const memoizedCallback = useCallback(\n      () =\u003e {\n        doSomething(a, b);\n      },\n      [a, b],\n    );\n```\n\n\u003e Returns a memoized callback.\n\n인라인 콜백과 입력 배열을 전달합니다. `useCallback` 은 입력 중 하나가 변경된 경우에만 변경되는 콜백의 `memoized` 버전을 반환합니다\n\n즉 `a` 또는 `b` 가 변경이 되었다면 함수 결과를 반환한다. \n\n- 동일한 표현\n\n```js\n    useCallback(fn, inputs) //is equivalent to \n    useMemo(() =\u003e fn, inputs).\n```\n\n\u003cbr/\u003e\n\n### **6. useMemo**\n\n```js\n    const memoizedValue = useMemo(() =\u003e computeExpensiveValue(a, b), [a, b]);\n```\n\n\u003e Returns a memoized value.\n\n**렌더링되는 동안** 에 `useMemo`에 전달된 함수가 실행이 된다.\n \n즉, 사용하는데 있어서 조심해야한다. 렌더링동안 하지않는 것을 하지 않아야한다. `side effect`를 위해 `useEffect`를 사용해야하는 것이다.\n\n`useEffect` 와 동일하게 배열이 넘어가지 않을 경우 매순간 첫번째 매개변수를 타게 된다.\n\n성능 최적화를 위해서 `useMemo`를 사용할 수 있을 것으로 생각이 된다.\n\n\u003cbr/\u003e\n\n### **7. useRef**\n\n```js\n    function TextInputWithFocusButton() {\n      const inputEl = useRef(null);\n      const onButtonClick = () =\u003e {\n        // `current` points to the mounted text input element\n        inputEl.current.focus();\n      };\n\n      return (\n        \u003c\u003e\n          \u003cinput ref={inputEl} type=\"text\" /\u003e\n          \u003cbutton onClick={onButtonClick}\u003eFocus the input\u003c/button\u003e\n        \u003c/\u003e);\n    }\n```\n\n\u003cbr/\u003e\n\n### **8. useImperativeHandle**\n\n```js\n    useImperativeHandle(ref, createHandle, [inputs])\n```\n\n`useImperativeHandle` 은 `ref` 를 사용할 때 부모 구성 요소에 노출되는 인스턴스 값을 사용자 정의합니다.\n\n```js\n    function FancyInput(props, ref) {\n      const inputRef = useRef();\n\n      useImperativeHandle(ref, () =\u003e ({\n        focus: () =\u003e {\n          inputRef.current.focus();\n        }\n      }));\n\n      return \u003cinput ref={inputRef} ... /\u003e;\n    }\n    FancyInput = forwardRef(FancyInput);\n```\n\n\u003cbr/\u003e\n\n### **9. useLayoutEffect**\n\n서명은 `useEffect`와 동일하지만 **모든 DOM 변이 후에 동기적으로 시작** 됩니다. \n\n`useLayoutEffect` 내부에서 예약된 업데이트는 브라우저가 페인트할 수 있기 전에 동기적으로 플러시됩니다.\n\n이것을 사용하여 **DOM에서 레이아웃을 읽고 동기적으로 다시 렌더링**합니다.\n\n시각적인 업데이트를 막지 않도록 `useEffect`를 사용하기를 권장함\n\n\u003cbr/\u003e\n\n### **10. useDebugValue**\n\n`useDebugValue`는 `React DevTools`에서 사용자 정의 후크 레이블을 표시하는 데 사용할 수 있습니다.\n\n```js\n    function useFriendStatus(friendID) {\n      const [isOnline, setIsOnline] = useState(null);\n    \n      // ...\n    \n      // Show a label in DevTools next to this Hook\n      // e.g. \"FriendStatus: Online\"\n      useDebugValue(isOnline ? 'Online' : 'Offline');\n    \n      return isOnline;\n    }\n```\n\n#### 10.1 Defer formatting debug values\n\n```js\n    useDebugValue(date, date =\u003e date.toDateString());\n```\n\n#### Reference\n\n-[usehooks](https://usehooks.com/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseonhyungjo%2Freact-hook-sample","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseonhyungjo%2Freact-hook-sample","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseonhyungjo%2Freact-hook-sample/lists"}