- React와 MVC 패턴
- 초기에는 MVC 디자인 패턴을 통해 주로 개발이 이루어졌다
- React에서 MVC의 문제점
- MVC 패턴은 Model과 View가 소통하며 서로 양방향으로 영향을 주며 데이터의 흐름을 가진다는 특징이 존재
- App의 규모가 커질수록 영향이 커져서 예측 불가능하고, 그에 따라 버그가 발생하기도 했음
(Facebook에서 대표적으로알람
기능 버그를 예시로 들었음)
- Flux 패턴의 등장
- Facebook 에서는 양뱡항의 데이터 흐름을 문제라고 생각하였고, 단방향 데이터 흐름 패턴을 만듬
- Flux 패턴을 구현한 구현체 중 가장 많이 사용되는 것이 바로 우리가 아는
Redux
- Flux 패턴의 단방향 흐름
- Dispatcher
Dispatcher는 Flux의 모든 데이터 흐름을 관리하는 허브 역할을 하는 부분입니다. Action이 발생되면 Dispatcher로 전달되는데, Dispatcher는 전달된 Action을 보고, 등록된 콜백 함수를 실행하여 Store에 데이터를 전달합니다. Dispatcher는 전체 어플리케이션에서 한 개의 인스턴스만 사용됩니다- Store
어플리케이션의 모든 상태 변경은 Store에 의해 결정이 됩니다. Dispatcher로 부터 메시지를 수신 받기 위해서는 Dispatcher에 콜백 함수를 등록해야 합니다. Store가 변경되면 View에 변경되었다는 사실을 알려주게 됩니다. Store은 싱글톤으로 관리됩니다.- View
Flux의 View는 화면에 나타내는 것 뿐만이나라, 자식 View로 데이터를 흘려 보내는 뷰 컨트롤러의 역할도 함께 합니다.- Action
Dispatcher에서 콜백 함수가 실행 되면 Store가 업데이트 되게 되는데, 이 콜백 함수를 실행 할 떼 데이터가 담겨 있는 객체가 인수로 전달 되어야 합니다. 이 전달 되는 객체를 Action이라고 하는데, Action은 대채로 액션 생성자(Action creator)에서 만들어집니다.
- React는 단방향 (부모 -> 자식)으로 값(props)를 전달한다
- 자식에서 부모에게 props를 전달할수는 없음
- 자식에서 부모의 state를 바꿀 수 있는 방법은 2가지가 존재
- 부모가 setState 함수를 props로 넘겨서 자식에서 호출하는 방법
--> 규모가 커질수록 관리가 힘들어짐
--> 그래서 등장한 것이 바로 아래의 Redux / mobx / Recoil- state management tool을 사용하는 방법(Redux / Recoil)
- Facebook에서 제안한 Flux 아키텍처를 사용한 구현체가 바로 Redux
- Redux는 처음 배우기에 러닝 커브가 높고, 복잡하며, React에 최적화 되어있지는 않다
--> 그래서 등장한 Recoil이 바로React를 위한 상태관리 라이브러리
이다!
- 정의 : React를 위한 상태 관리 라이브러리
- 핵심 개념
- atom
- 상태의 단위
- Redux의 store와 비슷한 역할
- atom은 업데이트와 구독이 가능
- atom을 업데이트 하면, atom을 구독한 모든 컴포넌트의 State는 업데이트 되고 re-render 수행
- selector
- atom이 가진 State를 가공하여 반환할 수 있음
- 파생된 State를 나타내는데 사용
- atoms 상태값을 동기 또는 비동기 방식을 통해 변환
- 비동기 수행을 할 수 있음(서버 통신에 사용 가능)
- 핵심 api
- useRecoilValue() : State의 Value
- useSetRecoilState() : State를 수정하는 setter
- useRecoilState() : State의 Value와 setter를 함께 사용할 수 있는 기능
- 비동기 작업의 상태를 관리하기 위해 Suspense / Loadable 사용 가능
- React.Suspense
- Recoil의 Loadable
- 추가적으로 React Query를 사용해서 해결할수도 있음
[ 기본적인 atom / selector 사용 ]
/* atom 생성 */ const textState = atom({ key: 'textState', // unique ID(다른 atom/selectors 와 구별하기 위함) default: '', // default value (=initial value) }) /* selector 생성 -> atom을 가공해서 사용 가능 */ const charCountState = selector({ key: 'charCountState', // unique ID (with respect to other atoms/selectors) get: ({get}) => { const text = get(textState); return text.length; }, }); /* atom 구독 */ function TextInput() { const [text,setText] = useRecoilState(textState); //const text = useRecoilValue(textState); //const setText = useSetRecoilValue(textState); const onChange = (event) => { setText(event.target.value); }; ... }
[ selector로 비동기 통신 ]
(state.js)
export const githubRepo = selectorFamily({ key: "github/get", get: (githubId) => async () => { if (!githubId) return ""; const { data } = await axios.get( `https://api.github.com/repos/${githubId}` ); return data; }, });
(Github.js)
import { useRecoilValue } from 'recoil'; import { selectorFamily } from '../../state'; const Github = () => { const githubId = 'juno7803'; const githubRepos = useRecoilValue(githubRepo(githubId)); return( <> <div>Repos : {githubRepos}</div> </> ) } export default Github;