항목 | Zustand | Context API |
종류 | 외부 상태 관리 라이브러리 | React 내장 기능 |
목적 | 전역 상태 간단하고 효율적으로 관리 | 전역 상태 전달 목적 (주입) |
상태 업데이트 | 구독 기반 (Selective rendering) | 모든 하위 컴포넌트가 리렌더링 |
복잡도 | 간단한 문법, 빠른 설정 | Provider/Consumer 구조 필요 |
의존성 | 있음 (zustand 설치 필요) | 없음 (React 내장) |
구독 기반?
📌 Context API
// CounterContext.tsx
const CounterContext = createContext(null)
const CounterProvider = ({ children }) => {
const [count, setCount] = useState(0)
const increase = () => setCount((c) => c + 1)
return (
<CounterContext.Provider value={{ count, increase }}>
{children}
</CounterContext.Provider>
)
}
// App.tsx
const Counter = () => {
const { count, increase } = useContext(CounterContext)
return <button onClick={increase}>{count}</button>
}
💡 특징: count나 increase 둘 중 하나만 필요해도, 둘 다 리렌더링됨
상태 기반이라 상태값이 바뀌면 참조하고 있는 모든 컴포넌트가 싹 랜더됨. 누가 사용하는지 정확히 따지지않고 그냥 다.
📌 Zustand
// store.ts
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
}))
// App.tsx
const Counter = () => {
const count = useStore((state) => state.count)
const increase = useStore((state) => state.increase)
return <button onClick={increase}>{count}</button>
}
💡 특징: useStore((state) => state.count)처럼 필요한 값만 구독해서 불필요한 렌더링을 방지
구독 기반이라 상태를 사용하는 컴포넌트가 "내가 씀!"하고 명시적으로 구독?함. 그래서 진짜로 쓰고잇는 걔만 리랜더함
좀 더 간단한 예시
const CountContext = createContext({ count: 0 })
const Counter = () => {
const { count } = useContext(CountContext)
return <div>{count}</div>
}
const useStore = create((set) => ({
count: 0,
text: "hello",
}))
const Counter = () => {// text가 바뀌어도 랜더안됨
const count = useStore((state) => state.count) // count만 구독했기때문
return <div>{count}</div>
}
예제 시나리오
- count와 text라는 두 상태가 있음
- CountComponent: count만 사용
- TextComponent: text만 사용
- 상태가 바뀔 때 각 컴포넌트가 리렌더되는지 콘솔로 확인
// App.tsx
import React, { createContext, useContext, useState } from "react";
const StateContext = createContext(null);
const Provider = ({ children }) => {
const [count, setCount] = useState(0);
const [text, setText] = useState("hello");
return (
<StateContext.Provider value={{ count, setCount, text, setText }}>
{children}
</StateContext.Provider>
);
};
const CountComponent = () => {
const { count } = useContext(StateContext);
console.log("🟠 CountComponent rendered");
return <div>Count: {count}</div>;
};
const TextComponent = () => {
const { text } = useContext(StateContext);
console.log("🔵 TextComponent rendered");
return <div>Text: {text}</div>;
};
export default function App() {
return (
<Provider>
<Buttons />
<CountComponent />
<TextComponent />
</Provider>
);
}
const Buttons = () => {
const { setCount, setText } = useContext(StateContext);
return (
<>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<button onClick={() => setText("updated!")}>Change Text</button>
</>
);
};
count만 바꿔도 TextComponent까지 같이 렌더링됨
// store.ts
import { create } from "zustand";
export const useStore = create((set) => ({
count: 0,
text: "hello",
setCount: () => set((state) => ({ count: state.count + 1 })),
setText: () => set(() => ({ text: "updated!" })),
}));
// App.tsx
import React from "react";
import { useStore } from "./store";
const CountComponent = () => {
const count = useStore((state) => state.count);
console.log("🟠 CountComponent rendered");
return <div>Count: {count}</div>;
};
const TextComponent = () => {
const text = useStore((state) => state.text);
console.log("🔵 TextComponent rendered");
return <div>Text: {text}</div>;
};
const Buttons = () => {
const setCount = useStore((state) => state.setCount);
const setText = useStore((state) => state.setText);
return (
<>
<button onClick={setCount}>+1</button>
<button onClick={setText}>Change Text</button>
</>
);
};
export default function App() {
return (
<div>
<Buttons />
<CountComponent />
<TextComponent />
</div>
);
}
- setCount() → CountComponent만 렌더
- setText() → TextComponent만 렌더
'JavaScript > React' 카테고리의 다른 글
d (0) | 2024.03.19 |
---|---|
[React] 라이브러리 없이 자동 슬라이드 구현 (0) | 2024.01.10 |
[React] input type date 아이콘 커스텀 (0) | 2023.12.27 |
[React] 자동 무한 롤링 슬라이드 구현 (0) | 2023.12.25 |
[React] 로딩바 구현 (0) | 2023.12.18 |