XState 및 React로 전역 상태를 관리하는 방법
This article has become part of the official XState docs!
많은 React 애플리케이션은 Redux으로 대중화된 Flux 아키텍처를 따릅니다. 이 설정은 몇 가지 주요 아이디어로 특징지을 수 있습니다.
dispatch
기능을 제공합니다. Redux는 이것을 actions
라고 부르지만 XState에서 알려진 대로 events
라고 부르겠습니다. 이 기사는 Flux 아키텍처가 좋은 생각인지에 대해서는 깊이 다루지 않습니다. David Khourshid의 기사는 여기에서 자세히 설명합니다. 이 문서의 목적을 위해 글로벌 스토어를 갖고 싶어하고 XState에서 이를 복제하려고 한다고 가정합니다.
그렇게 하고 싶은 데에는 여러 가지 이유가 있습니다. 복잡한 비동기 동작을 관리하고 어려운 문제를 모델링할 때 XState는 누구에게도 뒤지지 않습니다. Redux 앱에서 이를 관리하려면 일반적으로 redux-thunk , redux-loop 또는 redux-saga 미들웨어가 필요합니다. XState를 선택하면 복잡성을 관리할 수 있는 최고의 방법이 제공됩니다.
전 세계적으로 사용 가능한 매장
Redux의 전 세계적으로 사용 가능한 저장소를 모방하기 위해 React 컨텍스트를 사용할 것입니다. React 컨텍스트는 작업하기 까다로운 도구일 수 있습니다. 너무 자주 변경되는 값을 전달하면 트리 아래로 다시 렌더링될 수 있습니다. 즉, 가능한 한 적게 변경되는 값을 전달해야 합니다.
운 좋게도 XState는 이를 위한 최고의 방법을 제공합니다.
import React, { createContext } from 'react';
import { useInterpret } from '@xstate/react';
import { authMachine } from './authMachine';
import { ActorRefFrom } from 'xstate';
interface GlobalStateContextType {
authService: ActorRefFrom<typeof authMachine>;
}
export const GlobalStateContext = createContext(
// Typed this way to avoid TS errors,
// looks odd I know
{} as GlobalStateContextType,
);
export const GlobalStateProvider = (props) => {
const authService = useInterpret(authMachine);
return (
<GlobalStateContext.Provider value={{ authService }}>
{props.children}
</GlobalStateContext.Provider>
);
};
useInterpret
를 사용하면 구독할 수 있는 실행 중인 시스템에 대한 정적 참조인 service
가 반환됩니다. 이 값은 절대 변경되지 않으므로 낭비되는 재렌더링에 대해 걱정할 필요가 없습니다.컨텍스트 활용
트리 아래에서 다음과 같이 서비스에 가입할 수 있습니다.
import React, { useContext } from 'react';
import { GlobalStateContext } from './globalState';
import { useActor } from '@xstate/react';
export const SomeComponent = (props) => {
const globalServices = useContext(GlobalStateContext);
const [state] = useActor(globalServices.authService);
return state.matches('loggedIn') ? 'Logged In' : 'Logged Out';
};
useActor
후크는 서비스가 변경될 때마다 수신하고 state
값을 업데이트합니다.성능 향상
위의 구현에 문제가 있습니다. 이렇게 하면 서비스 변경 사항에 대한 구성 요소가 업데이트됩니다. Redux는 구성 요소를 다시 렌더링할 수 있는 상태 부분을 제한하는 기능인 선택기를 사용하여 상태를 유도하는 도구를 제공합니다.
운 좋게도 XState도 이를 제공합니다.
import React, { useContext } from 'react';
import { GlobalStateContext } from './globalState';
import { useSelector } from '@xstate/react';
const selector = (state) => {
return state.matches('loggedIn');
};
export const SomeComponent = (props) => {
const globalServices = useContext(GlobalStateContext);
const isLoggedIn = useSelector(globalServices.authService, selector);
return isLoggedIn ? 'Logged In' : 'Logged Out';
};
이제 이 구성 요소는
state.matches('loggedIn')
가 다른 값을 반환하는 경우에만 다시 렌더링됩니다. 이것은 성능을 최적화하려는 경우 useActor
에 대한 권장 접근 방식입니다.이벤트 전달
전역 저장소에 이벤트를 전달하기 위해 서비스의
send
함수를 직접 호출할 수 있습니다.import React, { useContext } from 'react';
import { GlobalStateContext } from './globalState';
export const SomeComponent = (props) => {
const globalServices = useContext(GlobalStateContext);
return (
<button onClick={() => globalServices.authService.send('LOG_OUT')}>
Log Out
</button>
);
};
이를 위해
useActor
를 호출할 필요가 없으며 컨텍스트에서 바로 사용할 수 있습니다.플럭스와의 편차
예리한 독자라면 이 구현이 Flux와 약간 다르다는 것을 알아차릴 수 있습니다. 예를 들어 단일 전역 저장소 대신
authService
, dataCacheService
및 globalTimeoutService
와 같이 한 번에 여러 대의 실행 중인 시스템이 있을 수 있습니다. 각각 고유한 속성send
도 있으므로 글로벌 디스패치를 호출하지 않습니다.이러한 변경 사항은 해결할 수 있습니다. 모든 서비스
send
기능을 수동으로 호출하는 글로벌 스토어 내부에 합성send
을 생성할 수 있습니다. 그러나 개인적으로 나는 내 메시지가 전달되는 서비스를 정확히 아는 것을 선호하며 이벤트를 전역적으로 네임스페이스로 유지하지 않아도 됩니다.요약
XState는 React 애플리케이션의 글로벌 저장소로 훌륭하게 작동할 수 있습니다. 응용 프로그램 논리를 같은 위치에 유지하고 부작용을 일급 시민으로 취급하며
useSelector
와 함께 우수한 성능을 제공합니다. Flux 아키텍처에 관심이 있지만 앱의 논리가 감당할 수 없다고 생각되는 경우 이 접근 방식을 선택해야 합니다.
Reference
이 문제에 관하여(XState 및 React로 전역 상태를 관리하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mattpocockuk/how-to-manage-global-state-with-xstate-and-react-3if5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)