useSyncExternalStore 📖
`useSyncExternalStore`는 외부 상태 store를 React 컴포넌트와 동기화해 concurrent reading(동시 읽기)를 지원할 수 있도록 하는 React Hook입니다.
❗쉽게 설명하면
react가 아닌 외부 저장소 (ex. 바닐라 자바스크립트) 값을 읽어드려 외부 저장소 값의 변화를 추적하고, 동시성 상태 변화에 대응할 수 있습니다.
기본 사용법
useSyncExternalStore은 세 가지 인자를 받아서 사용합니다.
import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';
function TodosApp() {
const todos = useSyncExternalStore(
todosStore.subscribe,
todosStore.getSnapshot,
todosStore.getServerSnapshot
);
// ...
}
`subscribe`: 하나의 `callback`인수를 받아 외부 저장소의 변화를 구독하는 함수입니다. 상태가 변할 때마다 인자로 받아온 `callback`을 호출합니다.
`getSnapshot`: 현재 상태의 스냅샷을 반환하는 함수입니다. 컴포넌트가 상태를 필요로 할 때 호출됩니다.
`getServerSnapshot (옵션)`: 서버 사이드 렌더링 환경에서 사용되는 스냅샷을 반환하는 함수입니다.
타입
useSyncExternalStore의 `Type`을 확인해 봤습니다.
export function useSyncExternalStore<Snapshot>(
subscribe: (onStoreChange: () => void) => () => void,
getSnapshot: () => Snapshot,
getServerSnapshot?: () => Snapshot,
): Snapshot;
subscribe는 `onStoreChange`함수를 인자로 받습니다. `onStoreChange`함수가 실행되면 변경된 상태를 렌더링 시켜주는 함수입니다.
❗쉽게 말하면
이 함수를 외부 저장소에 등록하고, 외부 저장소의 상태가 변화했을 때 `onStoreChange`함수를 실행시켜 주면 리액트에서 렌더링을 시켜준다는 말입니다.
사용 방법 📖
외부 저장소 만들기
간단하게 작성한 외부 저장소입니다.
function externalStore(initialState) {
let state = initialState;
const callbacks = new Set();
function subscribe(callback) {
callbacks.add(callback);
return () => callbacks.delete(callback);
}
function getState() {
return state;
}
function setState(update) {
state = typeof update === 'function' ? update(state) : update;
callbacks.forEach((callback) => callback());
}
return {
getState,
setState,
subscribe,
};
}
externalStore는 `initialState` 값을 받아와 state 변수에 저장합니다.
function externalStore(initialState) {
let state = initialState;
...
}
subscribe는 외부에서 `callback`함수를 받아와 callbacks변수에 저장하고 `callback`을 지우는 함수를 반환합니다.(구독 취소)
function externalStore(initialState) {
let state = initialState;
const callbacks = new Set();
function subscribe(callback) {
callbacks.add(callback);
return () => callbacks.delete(callback);
}
}
getState함수는 현재 상태를 반환하는 함수입니다.
function getState() {
return state;
}
setState함수는 매개변수로 함수 또는 값을 받아와 현재 상태를 변경하는 함수입니다.
function setState(update) {
state = typeof update === 'function' ? update(state) : update;
callbacks.forEach((callback) => callback()); // 변화를 알림
}
useStore
export function useStore(state) {
const store = useSyncExternalStore(
state.subscribe,
state.getState,
state.getState
)
return [store, state.setState]
}
useStore에 인자로 위에 생성한 외부 저장소 값이 들어가고, `useSyncExternalStore`에는 외부 저장소의 `subscribe``getState`를 넣어줍니다. 이렇게 간단한 전역 상태 라이브러리를 만들게 되었습니다.
todoList 만들기
위에서 만든 전역 상태 라이브러리를 이용해 간단한 todoList를 만들어보겠습니다.
const todoStore = externalStore([]);
export const useTodos = () => {
const [todos, setTodos] = useStore(todoStore)
const addTodo = (todo) => {
setTodos((prev) => [...prev, todo])
}
const deleteTodo = (todoId) => {
setTodos((prev) => prev.filter((todo) => todo.id !== todoId))
}
return {
todos,
addTodo,
deleteTodo
}
}
`useTodos` hook에 todo 전역 상태와, todo 상태를 변경시켜 주는 `addTodo``deleteTodo`를 만들었습니다.
{
const addTodo = (todo) => {
setTodos((prev) => [...prev, todo])
}
const deleteTodo = (todoId) => {
setTodos((prev) => prev.filter((todo) => todo.id !== todoId))
}
}
위에서 만든 externalStore를 사용해 `todoStore`를 생성하고, `useStore`hook에 넣어줘 useSyncExternalStore와 연결시켜 주었습니다.
const todoStore = externalStore([]);
export const useTodos = () => {
const [todos, setTodos] = useStore(todoStore)
...
}
todoList UI 만들기
간단한 UI를 만들어 다른 컴포넌트에서 하나의 전역 상태를 관리할 수 있게 만들었습니다.
import { useState } from "react";
import { useTodos } from "./useTodoStore";
export const Todos = () => {
const { todos } = useTodos();
return (
<div>
{todos.map(todo => (
<div key={todo.id}>{todo.title}</div>
))}
<AddTodo />
</div>
);
};
export const AddTodo = () => {
const { addTodo } = useTodos();
const [value, setValue] = useState('');
const onChange = (e) => {
setValue(e.target.value);
};
const onAddTodo = () => {
addTodo({
id: Math.random(),
title: value,
});
};
return (
<div>
<input value={value} onChange={onChange} />
<button onClick={onAddTodo}>추가</button>
</div>
);
};
출처 🏷️
https://ko.react.dev/reference/react/useSyncExternalStore#subscribing-to-a-browser-api
'공부 > react' 카테고리의 다른 글
라이브러리 없이 라우터(Router) 만들기 (0) | 2024.08.27 |
---|---|
React는 Hooks를 배열로 관리하는 이유 (0) | 2024.08.19 |
React 숙련 4 (react-router-dom) (0) | 2024.08.16 |
React 숙련 3 (Memoization) (0) | 2024.08.16 |
React 숙련 2 (react hooks) (1) | 2024.08.16 |
React 숙련 1 (styled-components) (0) | 2024.08.16 |
React 입문 6 (DOM, VirtualDOM) (0) | 2024.08.09 |
React 입문 5 (명령형, 선언형) (0) | 2024.08.09 |