728x90
그동안 리액트 상태 관리는 useState로 했었는데 이번에 개인 프로젝트를 하면서 Recoil를 써보려고 차근차근 알아가는 중이다.
Recoil 이란?
Recoil은 페이스북에서 만든 React 전역 상태관리 라이브러리이다.
Recoil 설치 및 사용법
1. Recoil 설치
// npm 사용 시
npm install recoil
// yarn 사용 시
yarn add recoil
2. RecoilRoot
- Recoil은 React의 상태 관리 라이브러리이기 때문에 (index.js 또는 App.js 파일에서) RecoilRoot로 애플리케이션 전체를 감싸야한다.
- RecoilRoot는 상태 관리를 위한 컨텍스트를 제공한다.
// index.js
import "antd/dist/antd.css"; // Ant Design 기본 스타일
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { RecoilRoot } from "recoil";
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter basename="/프로젝트경로">
<RecoilRoot> -> 여기
<App />
</RecoilRoot>
</BrowserRouter>
</React.StrictMode>
);
3. Atom 생성
근데 이름이 왜 Atom 일까? (갑자기 궁금증이 들어 찾아보았다.)
Recoil에서 사용하는 Atom이라는 용어는 원자(Atom)라는 개념에서 유래되었다고 한다.
원자(Atom)는 시스템의 기본 단위로, 독립적이고 변하지 않는 상태를 표현하며, 이를 통해 상태 관리를 간결하고 효율적으로 할 수 있도록 설계되었다.
- Recoil에서는 Atom을 사용하여 상태를 정의한다. 이 Atom은 전역 상태의 일종으로, 애플리케이션의 여러 컴포넌트에서 공유될 수 있다.
- Atom은 별도의 파일로 정의하는 것이 일반적이다.
import { atom } from 'recoil';
export const textState = atom({
key: 'textState', // 고유한 키
default: '', // 초기값
});
Atom key와 default 역할
1. 고유한 키 (key)
☞ key는 atom의 고유 식별자이다.
☞ Recoil이 여러 atom을 식별하고 관리하는 데 사용된다.
☞ 문자열로 제공되며, 애플리케이션 내에서 유일해야 한다. 동일한 key를 가진 atom이 두 개 이상 존재할 수 없기 때문에, 키는 고유성을 유지해야 한다.
2. 초기값 (default)
☞ default는 atom의 초기 상태를 정의한다.
☞ 컴포넌트가 이 atom을 처음 구독할 때, 이 초기값이 사용된다.
☞ 기본적으로 어떤 데이터 유형이든 가능하며, 문자열, 숫자, 객체, 배열 등 다양한 형태의 값을 지정할 수 있다.
4. Atom 구독, 상태 읽기 및 업데이트
컴포넌트에서 atom을 구독하여 상태를 가져오고, 상태를 변경할 수 있다.
Recoil에서는 useRecoilState, useRecoilValue, 또는 useSetRecoilState와 같은 훅을 사용하여 atom에 접근한다.
// useRecoilState를 사용하여 atom의 값을 읽고 업데이트하는 방법
import React from 'react';
import { useRecoilState } from 'recoil';
import { textState } from './atom을 정의한 파일경로'; // atom을 가져옴
function TextInput() {
const [text, setText] = useRecoilState(textState); // atom 구독
const onChange = (event) => {
setText(event.target.value); // 상태 업데이트
};
return (
<div>
<input type="text" value={text} onChange={onChange} />
<p>You typed: {text}</p>
</div>
);
}
export default TextInput;
5. Selector 사용하기 (옵션)
Recoil에서는 selector를 사용하여 파생된 상태를 생성할 수 있다.
selector는 atom에서 계산된 값을 반환하는 함수이다.
// 아톰 정의
import { selector } from 'recoil';
import { textState } from './state';
export const charCountState = selector({
key: 'charCountState',
get: ({ get }) => {
const text = get(textState);
return text.length;
},
});
// 컴포넌트에서 selector를 useRecoilValue로 가져와 사용할 수 있다
import React from 'react';
import { useRecoilValue } from 'recoil';
import { charCountState } from './state';
function CharacterCount() {
const count = useRecoilValue(charCountState);
return <p>Character Count: {count}</p>;
}
export default CharacterCount;
Recoil에서 상태 관리할 때 주로 사용하는 훅
1. useRecoilState
- atom의 상태를 읽고, 업데이트할 수 있는 훅이다.
- 상태를 읽고, 그 상태를 직접 수정해야 하는 경우에 사용한다.
// text는 atom의 현재 상태 값을 나타내며, setText를 사용하여 상태를 업데이트 한다
const [text, setText] = useRecoilState(textState);
2. useRecoilValue
- atom이나 selector의 현재 값을 읽기 위한 훅이다. 상태를 읽기만 하고 업데이트할 필요가 없는 경우에 사용된다.
- 상태를 조회하여 UI에 표시하거나 다른 계산에 사용할 때 유용하다.
// text는 atom의 현재 상태 값을 가져오며, 상태를 업데이트하는 기능은 없다.
const text = useRecoilValue(textState);
3. useSetRecoilState
- atom의 상태를 업데이트하기 위한 전용 훅이다.
- 상태를 읽지 않고, 오로지 업데이트만 필요할 때 유용하다.
// setText를 사용하여 atom의 상태를 업데이트할 수 있다.
const setText = useSetRecoilState(textState);
4. useRecoilCallback
- Recoil 상태를 읽고 업데이트하는 콜백 함수를 만드는 데 사용된다.
- 주로 컴포넌트 외부에서 Recoil 상태에 접근하거나, 비동기 작업이 필요할 때 유용하다.
/**
* snapshot: 현재 상태의 스냅샷을 나타내며, 상태를 읽을 때 사용된다.
* snapshot.getLoadable(textState).contents
* - snapshot.getLoadable은 주어진 리코일 상태(textState)의 값을 가져오는 함수이다.
* - 이 값은 Loadable 객체 형태로 반환되며,
* - Loadable은 리코일 상태가 비동기일 경우 상태를 loading, hasValue, hasError 중 하나로 표시한다.
* - .contents는 그 상태의 실제 값을 얻기 위한 속성이다.아래의 코드에서 textState의 현재 값을 읽는다.
* set: Recoil 상태를 업데이트하는 함수로, textState의 값을 'new value'로 변경 한다.
*
* 동작 요약
* snapshot: 현재 textState의 값을 가져와서
* set: textState의 값을 'new value'로 업데이트
*/
const callback = useRecoilCallback(({ snapshot, set }) => () => {
const currentValue = snapshot.getLoadable(textState).contents;
set(textState, 'new value');
});
5. useRecoilTransaction_UNSTABLE
- 여러 atom의 상태를 동시에 업데이트할 수 있는 기능을 제공한다. (이 훅은 아직 안정화되지 않았음)
/**
* set(textState, newValue): textState의 값을 newValue로 설정 한다.
* set(anotherState, newValue * 2): anotherState의 값을 newValue의 두배로 설정 한다.
*
* 동작 요약
* newValue를 받아서 textState와 anotherState 두 상태를 각각 newValue와
* newValue * 2로 동시에 업데이트한다.
*/
const setValue = useRecoilTransaction_UNSTABLE(({ set }) => (newValue) => {
set(textState, newValue);
set(anotherState, newValue * 2);
});
※ 간단 요약
useRecoilState: 상태를 읽고 업데이트
useRecoilValue: 상태를 읽기만 할 때
useSetRecoilState: 상태를 업데이트만 할 때
useRecoilCallback: 컴포넌트 외부에서 상태를 읽고 업데이트하는 콜백 생성
useRecoilTransaction_UNSTABLE: 여러 atom의 상태를 동시에 업데이트
useState와 Recoil의 차이점
구분 | useState | Recoil |
상태 관리 범위 | - 상태는 해당 컴포넌트 내부에서만 유효 - 컴포넌트가 다시 렌더링될 때, 그 컴포넌트의 상태만 영향을 받음 |
- 상태는 애플리케이션의 여러 컴포넌트에서 공유 가능 - 여러 컴포넌트가 동일한 atom을 구독하며, atom의 상태가 변경되면 이를 사용하는 모든 컴포넌트가 자동으로 업데이트됨 |
상태 업데이트 방식 | - 상태를 업데이트하려면 해당 컴포넌트의 상태 업데이트 함수를 호출해야 함 - 상태를 가진 컴포넌트만 리렌더링됨 |
- atom의 상태는 useRecoilState를 사용하여 업데이트 가능 - 상태 변경이 발생하면 해당 atom을 참조하는 모든 컴포넌트가 리렌더링됨 |
파생 상태 | - 파생 상태를 만들기 위해 상태를 기반으로 새 상태를 계산해야 함 - useEffect와 함께 사용 가능 |
- selector를 사용하여 atom의 값을 기반으로 한 파생 상태를 쉽게 생성 가능 - selector는 자동으로 업데이트되며, 이를 사용하는 컴포넌트가 리렌더링됨 |
상태 관리의 복잡성 | - 상태가 간단할 경우 useState가 직관적이고 간편함 - 복잡한 상태 구조를 관리할 때 여러 개의 상태 변수를 만들어야 하므로 코드가 복잡해질 수 있음 |
- 복잡한 상태를 구조화된 방식으로 관리 가능 - atom과 selector를 조합하여 복잡한 상태 구조를 쉽게 처리할 수 있음 |
전역 상태 관리 | - 전역 상태를 관리하려면 Context API나 Redux 등의 다른 상태 관리 솔루션 필요 | - 기본적으로 전역 상태 관리 기능을 제공 - 여러 컴포넌트 간의 상태 공유가 용이함 |
비동기 처리 | - 비동기 데이터 처리와 함께 상태를 업데이트하려면 useEffect와 결합해야 함 | - selector를 통해 비동기 요청을 쉽게 처리 가능 - selector에서 비동기 함수를 반환하여 상태를 관리할 수 있음 |
이해한 대로 적어보는 useState와 Recoil의 장단점과 코드의 차이
useState
장점
- 상태가 해당 컴포넌트에서만 유효하므로 직관적이고 간편함
- 다른 컴포넌트의 영향을 받지 않음
단점
- 동일한 초기값을 가진 상태를 여러 컴포넌트에 중복으로 정의해야 함
// 예시 : default 값이 "" 인 상태가 필요한 상황이라고 했을때
// 상태 정의
// aa.js
const [test, setTest] = useState("");
// bb.js
const [test, setTest] = useState("");
// 값 읽기
console.log(test) // test 변수 사용(초기값은 "")
//초기값을 다르게 설정하려면
const [test, setTest] = useState("apple"); // test 초기값 : apple
// aa.js 값 변경
setTest("melon"); // 값 변경 후 test값 : melon
// bb.js
// test의 값은 여전히 "", 왜? : 상태는 해당 컴포넌트 내부에서만 유효하기때문에
Recoil
장점
- 상태를 여러 컴포넌트에서 공유할 수 있어, 상태 정의의 중복을 방지할 수 있음
- 중앙 집중화된 관리로 유지보수성이 높음
단점
- atom 값 변경 시 모든 참조 컴포넌트의 상태가 함께 변경됨. 따라서 의도치 않은 변경에 주의가 필요함
// 예시 : default 값이 "" 인 상태가 필요한 상황이라고 했을때
// 1. atom을 모두 정의해놓을 RecoilStatus.js 만들고 그 안에 atom 정의
export const commonEmptyStringState = atom({
// key는 고유키로 설정하면되고 우리가 가져다 쓰지 않음(Recoil이 여러 atom을 식별,관리하는 데 사용)
key: "commonEmptyString", // 고유한 식별자
default: "", // 초기값: 빈 문자열, 숫자등 필요한 타입 작성
});
// 각 js에서 atom 구독
// aa.js
import { useRecoilState} from "recoil";
import { commonEmptyStringState} from "/RecoilStatus.js경로";
const MainHome = () => {
const [text, setText] = useRecoilState(commonEmptyStringState); // atom을 구독
}
// bb.js
import { useRecoilState} from "recoil";
import { commonEmptyStringState} from "/RecoilStatus.js경로";
const MainHome = () => {
const [text, setText] = useRecoilState(commonEmptyStringState); // atom을 구독
}
// 값 읽기
console.log(test) // test 변수 사용(초기값은 "")
//초기값을 다르게 설정하려면 atom 추가 or 변경
export const commonEmptyStringState = atom({
key: "commonEmptyString", // 고유한 식별자
default: "aa",
});
console.log(test) // test 초기값 : aa
// aa.js 값 수정
setText("apple");
/*
* 수정 후
* aa.js text 값 : "apple"
* bb.js text 값 : "apple"
* 왜? : atom의 상태가 변경되면 이를 사용하는 모든 컴포넌트가 자동으로 업데이트됨
*/
728x90
'React > 기초' 카테고리의 다른 글
[React] MUI의 useTheme와 useMediaQuery로 구현하는 반응형 웹 (feat. 그 외 방법) (1) | 2024.10.12 |
---|---|
npm i create-react-app 와 npx create-react-app my-app 의 차이점 (1) | 2024.10.06 |
DOM 이란? & React와 ReactDOM 차이 (0) | 2024.07.13 |
리액트 실행 흐름 [ Index.js, App.js, Index.html ] (0) | 2024.07.12 |
create-react-app(CRA) 정리 (0) | 2024.07.08 |