ex )
export const React = (() => { let hook = []; let index = 0; function useState(initialValue) { const state = hook[index] || initialValue; const currentIndex = index; const setState = newValue => { hook[currentIndex] = newValue; }; index++; return [state, setState]; } function render(component) { index = 0; const C = component(); return C; } return { useState, render, }; })();
먼저 카운터 컴포넌트를 정의합니다.
function Counter() { const [count, setCount] = React.useState(0); return { click: () => setCount(count + 1), render: () => console.log('count:', count) }; }
컴포넌트를 렌더링합니다.
let App = React.render(Counter);
초기 상태를 출력합니다.
App.render(); // 출력: count: 0
클릭 이벤트를 시뮬레이션합니다.
App.click();
변경된 상태로 다시 렌더링합니다.
App.render(); // 출력: count: 1
할일 목록 컴포넌트
function TodoList() { const [todos, setTodos] = React.useState([]); const [inputText, setInputText] = React.useState(''); return { addTodo: (text) => { setTodos([...todos, text]); setInputText(''); }, updateInput: (text) => { setInputText(text); }, render: () => { console.log('Current todos:', todos); console.log('Input value:', inputText); } }; }
사용 예시
let TodoApp = React.render(TodoList); TodoApp.render(); // 초기 상태 출력
입력값 업데이트
TodoApp.updateInput('새로운 할일'); TodoApp = React.render(TodoList); TodoApp.render();
할일 추가
TodoApp.addTodo('새로운 할일'); TodoApp = React.render(TodoList); TodoApp.render();

이러한 구조를 통해 React는 렌더링 작업을 더 효율적으로 관리하고 사용자 경험을 개선할 수 있습니다.

1. child : 자식 노드를 가리킵니다.
2. sibling : 같은 부모를 가진 형제 Node를 가리킵니다.
3. return : 부모 Node를 가리킵니다.(작업 완료 후 돌아갈 노드)
const [상태관리 값, 변경된 것을 받아서 처리하는 상태관리 값] = useState(초기값)const [state, setState]=useState(initState)const UseStateComponent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return <button onClick={handleClick}>Count : {count}</button>; }; export default UseStateComponent;
컴포넌트가 렌더링이 된 후에 실행됩니다. 해당 컴포넌트에서 useEffect가장 늦게 읽힌다.
useEffect(()=>{useEffect가 실행되면 동작할 부분},[의존성 배열])
의존성 배열은 useEffect가 랜더링이 언제 일어날지를 알려주는 것입니다.
의존성 배열의 값이 변경될때마다 useEffect는 실행이 됩니다.
cleanup는 unMounting 일때 발생합니다.
cleanup 함수를 반환하여 정리 작업을 수행할 수 있습니다.
const UseEffectComponent = () => { const [name, setName] = useState('korea'); const [count, setCount] = useState(0); const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight, }); console.log('코드가 읽힙니다.'); useEffect(() => { // state와 setState동시에 사용하는 경우에는 의존성배열 사용된 어떠한 state값도 넣지말라 넣으면 무한 랜더링이 진행된다. setCount(count + 1); }, [count]); // 의존성배열이 반값이라면 딱 첫 랜더링이후에 읽혀지는 것만 진행 // 의존성배열이 없다면 그것은 그냥 useState와 동일하게 동작합니다. useEffect(() => { console.log('name effect'); }, [name]); console.log('코드가 전부 읽혔습니다.'); return ( <div> <h2>현재 윈도우 사이즈 :</h2> <p>width :{windowSize.width}px</p> <p>height :{windowSize.height}px</p> <p onClick={() => setName('KOREAKROEA')}>버튼</p> </div> ); }; export default UseEffectComponent;
const UseRefComponent = () => { // const [count, setCount] = useState(0); const [render, setRender] = useState(0); const countRef = useRef(0); // const handleCountUpdate = () => { // setCount(count + 1); // }; const handleRenderUpdate = () => { setRender(render + 1); }; // Ref는 아무리 수정해도 컴포넌트를 re-rendering을 시키지 않는다. const handleRefUpdate = () => { countRef.current = countRef.current + 1; console.log('Ref값 올라가유 : ', countRef); }; console.log('렌더링을 합니다 🎢'); // console.log("Ref값 올라갈까유? : ", countRef); return ( <div style={{ backgroundColor: 'lightblue', fontSize: '20px' }}> <p>State : {render}</p> <p>useRef : {countRef.current}</p> {/* <button onClick={handleCountUpdate}>state값 올라갑니다</button> */} <button onClick={handleRenderUpdate}>리 랜더링 되유 값이 이제 바껴유</button> <button onClick={handleRefUpdate}>ref값 올라갑니다</button> </div> ); }; export default UseRefComponent;
context로 관리하면 성능 문제가 발생합니다.context를 중첩해서 사용할 경우 컴포넌트 트리가 더욱더 복잡해 질 수 있습니다.Custom Hook은 React의 기본 Hook(useState, useEffect 등)을 조합하여 재사용 가능한 로직을 캡슐화한 함수입니다. 특정 기능을 여러 컴포넌트에서 반복적으로 사용할 때 유용합니다.
이름은 반드시 use로 시작해야 함 → React가 Hook으로 인식하도록
1. 컴포넌트 또는 다른 Hook 내부에서 호출해야 함
2. React의 규칙을 준수해야 함 → 조건문이나 반복문 안에서 호출하지 않기
import { useState } from "react"; export const useInput = (initialValue) => { const [value, setValue] = useState(initialValue); const handleChange = (event) => setValue(event.target.value); return [value, handleChange, setValue]; };
import React from "react"; import React from "react"; import { useInput } from "./useInput"; export const MyComponent = () => { const [name, handleNameChange] = useInput(""); return ( <input type="text" value={name} onChange={handleNameChange} /> ); };
import { useFetch } from "./customHook/useFetch"; const FETCH_URL = "https://jsonplaceholder.typicode.com/posts"; const FETCH_URL_TODO = "https://jsonplaceholder.typicode.com/todos"; const CustomHookFetch = () => { const { data, isLoading, error } = useFetch(FETCH_URL); if (isLoading) return ( <div style={{ display: "flex", width: "100%", height: "100vh", justifyContent: "center", alignItems: "center" }}> Loading... </div> ); if (error) return <div>Error</div>; return ( <div> <ul> {data.map((post) => ( <li key={post.id}> <h3>제목 : {post.title}</h3> <p>내용 : {post.body}</p> </li> ))} </ul> </div> ); }; export default CustomHookFetch;