본문 바로가기

웹공부/React

React에서 Recoil로 상태관리하기

 

상태 관리 라이브러리 Recoil에 대해 학습해보자.

Recoil: A state management library for React

 

Recoil

A state management library for React.

recoiljs.org


서론

 

왜 State 관리가 필요한가?

->다크모드/라이트모드 스위치를 만들며 이해해보기

 

1) index.tsx 파일에 적용되어있는 themeProvider 옮기기

왜 index.tsx -> app.tsx로 옮겼나면, theme을 state로 관리하기 위해서다.

 

-index.tsx에 있던 ThemeProvider를 App.js로 이전

-(theme.tsx) Theme = > lightTheme, darkTheme 두가지 버전으로 확장

 

//theme을 app.tsx에서의 state로 관리하기 위한 선언
const [isDark, setIsDark] = {false}

//theme의 state에 따른 테마색 분기
theme ={isDark? darkTheme : lightTheme}

//모드 전환 함수 + 테마 모드 변경하는 버튼 생성
const toggleDark () => setIsDart((current)=>!current);
<button onClick ={toggleDark}>Toggle Mode</button>

 

이와 같은 수정사항을 거침으로써 React 페이지 내부에서의 상태 관리를 통해 darkMode / lightMode 설정에 대한 구현을 완료할 수 있었다.

 

->Coins.tsx 페이지로 와서 button 위치 옮기기

Coins.tsx 페이지는 Router.tsx 페이지 안에 있으므로, App.tsx 페이지에서 정의했던 toggleDark function을

props로 내리전달해야한다.

 

+ type정의도 필요하다.

 

[Coin.tsx]

Coin.tsx에서 Coins 컴포넌트로부터 받아온 props의 type을 interface로 정의해주는 과정이 필요하다.

Interface ICoinProps {

isDark: boolean;

}


function Coin({isDark}: ICoinProps)

 

Chart에도 Dark 모드, Light 모드를 적용하기 위해서 App -> Router -> Coin -> Chart 컴포넌트까지 props를 자식컴포넌트로 전달 전달 전달 해서 내려줘야 한다.

 

 

지금까지 App.tsx -> Router.tsx -> Coin.tsx -> Chart.tsx를 타고 타고 타고 들어가서 isDark 속성을 props로 연속 내림 하여

theme에대한 것을 모두 적용할 수 있었다.

 

단계가 굉장히 많고 복잡하다. 어플리케이션이 복잡해질수록 복잡함은 배가 될 것이다.

개발자관점에서 그닥 좋은 코드가 아니라는 소리. 이럴 때 Recoil 라이브러리를 사용하면 코드를 깔끔하게 수정할 수 있다.

 

2개의 다른 컴포넌트, 2개의 다른 스크린이 있는데 같은 state에 접근해야 하고, 수정도 할 수 있어야 할때 전체상태관리를 통해 여러 단계를 거치지 않고 모든 계층에서(컴포넌트가 어디에 위치하느냐에 관계 없이) 한번에 접근할 수 있다면 아주 좋을 것이다!

 

global state개념을 사용해 어느계층에서나 props에 더 쉽게 접근할 수 있는 방법에 대해 알아보자!

 

global state

 

어플리케이션 전체에서 공유되는 state

무언가를 어플리케이션 전체에서 접근해야 할 때(ex) 사용자의 로그인 여부)

 

 

유저가 로그인해야지 Chart를 볼 수 있도록 하게끔 설정해보자.

isLoggedIn을 App으로부터 Chart까지 공유해줘야 한다. 

 

const [isLoggedIn, setIsLoggedIn] = useState(false)

 


1. Recoil 시작

1. 1 Recoil 설치하기

npm install recoil

1.2 Recoil 적용

index.tsx파일에서 App 컴포넌트를 <RecoilRoot> 컴포넌트로 감싸야 Recoil을 사용할 수 있다.

 

2. Recoil 주요 개념

Recoil을 사용하면 atoms(공유 상태)에서 selectors(순수 함수)를 거쳐 React 컴포넌트로 내려가는 data-flow graph를 만들 수 있다고 한다. selectors는 atoms 상태 값을 동기 혹은 비동기 방식을 통해 변환한다.

atoms, selectors는 각각 무슨 말이고, 해당 문장의 어떤 의미인지 알아보도록 하자. 

 

 

1) Atoms

Atoms는 상태 단위이며, 업데이트와 구독이 가능하다. atom이 업데이트되면(=변경이 일어나면) 각 구독된 컴포넌트는 새 값을 반영하기 위해 다시 렌더링된다. atoms는 runtime에서 생성될 수도 있다. atoms는 로컬 컴포넌트 상태 대신 사용할 수 있으며, 동일한 atom이 여러 컴포넌트에서 사용되는 경우 모든 컴포넌트는 상태를 공유한다.

 

쉽게 생각하면, props를 저장할 bubble을 만든다고 보면 된다.

아래 사진과 같이 접근하고자 하는 prop을 bubble에 저장해 어디서든 접근할 수 있도록 하는 것이다.

부모의 부모로부터 props를 받아올 필요 없이 즉시 가져오면 돼서 아주 편하다.

cosnt fontSizeState = atom({
	key:'fontSizeState',
    default: 14,
})

 

컴포넌트에서 atom을 읽고 쓸때는 useRecoilState라는 훅을  사용한다. React에서 쓰는 useState 훅과 비슷하지만, 상태가 컴포넌트간에 공유될 수 있다는 차이점이 있다. 아래 함수는 버튼 클릭 시 글꼴 크기가 1만큼 증가하며, fontSizeState atom을 사용하는 다른 컴포넌트의 글꼴 크기도 함께 변화한다.

function FontButton() {
	const [fontSize, setFontSize] = useRecoilState(fontSizeState);
    return (
    	<button onClick={()=>setFontSize((size)=>size+1)} style={{fontSize}}>
        	Click Me!
        </ buton>
    );
 }
function Text() {
	const [fontSize, setFontSize] = useRecoilState(fontSizeState);
    return <p style={{fontSize}}>This text will increase in size too.</p>
}

 

 

2) Selectors

selector 함수를 사용해 정의한다.

Selectors는 atoms나 다른 selectors를 입력으로 받아들이는 순수 함수(pure function)이다. 상위 atoms 혹은 select가 업데이트되면 반드시 하위의 selector 함수도 다시 실행된다.

컴포넌트들은 selectors를 atoms처럼 구독할 수 있고, selectors가 변경되면 컴포넌트들도 다시 렌더링된다.

selector는 다른 요소들을 입력으로 받아들이는 함수이므로, 상태를 기반으로 하는 파생 데이터를 계산하는 데 사용된다.

컴포넌트 관점에서 보면 selectors와 atoms는 동일한 인터페이스를 가지므로 서로 대체 가능하다.

const fontSizeLabelState = selector({
	key:"fontSizeLabelState",
    get: ({get})=>{
    	fontSize = get(fontSizeState);
        const unit='px';
        
        return `${fontSize}${unit};
    },
})

위 fontSizeLabelState 함수는 fontSizeState라는 하나의 atom에 의존성을 갖는다.fontSizeLabelState selector는 writable하지 않기 때문에 useRecoilState()를 사용하지 않았다.

selector는 이뿐만 아니라 useRecoilValue()를 사용해 읽을 수 있다. useRecoilValue()는 하나의 atom이나 selector를 인자로 받아 대응하는 값을 반환한다. 

function FontButton() {
	const [fontSize, setFontSize] = useRecoilState(fontSizeState);
    const fontSizeLabel = useRecoilValue(fontSizeLabelState);
    
    return (
    <>
    	<div>Current font size: ${fontSizeLabel}</div>
        
        <button onClick={setFontSize(fontSize+1)} style={{fontSize}}>
        	Click to Enlarge
        </button>
    </>
    );
}

 

해당 함수는 버튼을 클릭하면 버튼의 글꼴 크기가 증가하는 동시에 글꼴 크기를 반영하도록 글꼴 크기 레이블을 2번 업데이트하는 두가지 작업이 수행된다.

 


위에서 자식컴포넌트로 전달했던 'isDark' props를 Recoil을 사용하여 리팩토링 해보자.

 

우선, 위에서 작성했던 isDark 변수와, 자식컴포넌트에 props를 통해 전달했던 부분을 모두 지워준다.

 

 

1) Atom 생성하기

src> atom.ts 파일을 생성해, isDark 값(boolean)값을 저장할 atom을 생성하는 코드를 생성한다.

import {atom} from "recoil";

export const isDarkAtom =atom({
    key:"isDark",
    default: false,
})

 

 

2) App.tsx에서 isDark를 atom에서 읽어오기

useRecoilValue 훅을 사용해 위에서 정의했던 isDarkAtom의 isDark 값을 읽어오는 것으로 코드를 수정한다.

function App(){
  const isDark = useRecoilValue(isDarkAtom);
  //<>은 fragment = 유령 컴포넌트로, 부모 없이 서로 붙어있는 것들을 return할 수 있음.
  return <>
  <ThemeProvider theme={isDark?darkTheme:lightTheme}>
  <GlobalStyle />
  <Router /> 
  <ReactQueryDevtools initialIsOpen={true}/>
  </ThemeProvider>
  </>;
}

 

 

3) Chart.tsx 수정하기

Chart에서도 테마 모드별 그래프 색이 달라지도록 테마를 적용해야 하므로, App 컴포넌트에서 했던 것처럼

useRecoilValue 훅을 이용해 isDark를 불러온다.

function Chart({coinId}:ChartProps,){
    const isDark = useRecoilValue(isDarkAtom);
    
    ...일부 중략...
   	options={{
    	...일부 중략...
        ,theme:{
        	mode: isDark? "dark" : "light"
        },
        ...일부 중략...
    }}
    
 }

 

 

4) Coins.tsx에서 toggle 버튼 변겅하기

toggle 버튼을 누를 시, 테마 색이 바뀌어야 하므로,

atom을 수정하기 위한 useSetRecoilState 훅을 사용해야한다.

useSetRecoilState의 매개변수로 atom 객체를 받아 해당 값을 변경해야한다.

const setterDarkAtom = useSetRecoilState(isDarkAtom);
const toggleDarkAtom = () => setterDarkAtom((prev)=>!prev)

 

위와 같이 마치 useState의 setter 함수처럼 toggle 함수를 정의한 후,

 

	  <Header>
            <Title>Coin</Title>
            <ToggleBtn onClick={toggleDarkAtom}>Toggle Mode</ToggleBtn>
        </Header>

 

button의 onClick event에 toggleDarkAtom 함수를 지정한다.

 

 


 

위와 같은 변경사항들을 적용해주고 나면, recoil을 이용해 전역상태관리되는

암호화폐 시가 조회 페이지를 완성할 수 있다!!

'웹공부 > React' 카테고리의 다른 글

React에서 react-hook-form 사용하기  (0) 2024.03.13
React에서 typescript 사용하기  (0) 2024.01.16
React에서 styled components 사용하기  (1) 2024.01.08
React 공부 9일차  (0) 2023.08.22
React 공부 8일차  (0) 2023.08.21