Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@adamkl
Created February 10, 2020 22:20
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adamkl/656008691d42220eddf8bfe147753063 to your computer and use it in GitHub Desktop.
Save adamkl/656008691d42220eddf8bfe147753063 to your computer and use it in GitHub Desktop.
xState service layer
import React from "react";
import { createUserSessionService } from "services/UserSessionService";
import { createNavService } from "services/NavService";
// Wiring up our "IOC container"
const userSessionService = createUserSessionService();
// NavService depends on UserSessionService
const navService = createNavService(userSessionService);
const dependencies = {
userSessionService,
navService
};
const AppContext = React.createContext(dependencies);
const AppContextProvider: React.FC = props => {
return (
<AppContext.Provider value={dependencies}>
{props.children}
</AppContext.Provider>
);
};
export { AppContext, AppContextProvider };
import React from "react";
import ReactDOM from "react-dom";
import App from "App";
import { AppContextProvider } from "AppContextProvider";
// Wrapping our app in our "IOC container"
ReactDOM.render(
<AppContextProvider>
<App />
</AppContextProvider>,
document.getElementById("root")
);
import React from "react";
import { useHistory } from "react-router-dom";
import { SideNav } from "SideNav";
import { useNavService } from "services/NavService";
const Navbar: React.FC = () => {
const [navState] = useNavService(); // Inject dependency
// Links automatically change based on authentication state
// from UserSessionService
const { links } = navState.context;
return (
<>
<SideNav
menuItems={links}
menuHeaderLink="/"
menuHeaderLinkAlt="Home"
/>
</>
);
};
export default Navbar;
import { useContext } from "react";
import { Machine, interpret, Interpreter } from "xstate";
import { useService } from "@xstate/react";
import { AppContext } from "AppContextProvider";
import { config, NavContext, NavEvent, NavStateSchema} from "./machine";
import { UserSessionService } from "services/UserSessionService";
// Custom hook for "dependency injection"
export const useNavService = () => {
const { navService } = useContext(AppContext);
return useService(navService);
};
export const createNavService = (userSessionService: UserSessionService): NavService => {
const navMachine = Machine<NavContext, NavStateSchema, NavEvent>(config);
const navService = interpret(navMachine).start();
// wire up state transition dependency between
// NavService and UserSessionService
userSessionService.onTransition(state => {
if (state.matches("AUTHENTICATED")) {
navService.send("SWITCH_TO_AUTHENTICATED_LINKS");
} else {
navService.send("SWITCH_TO_UNAUTHENTICATED_LINKS");
}
});
return navService;
};
import { useContext } from "react";
import { Machine, interpret, Interpreter } from "xstate";
import { useService } from "@xstate/react";
import { AppContext } from "AppContextProvider";
import { config, UserSessionContext, UserSessionStateSchema, UserSessionEvent } from "./machine";
// Custom hook for "dependency injection"
export const useUserSessionService = () => {
const { userSessionService } = useContext(AppContext);
return useService(userSessionService);
};
export const createUserSessionService = (): UserSessionService => {
const userSessionMachine = Machine<UserSessionContext, UserSessionStateSchema, UserSessionEvent>(config);
return (
interpret(userSessionMachine)
.start()
);
};
@veeral-patel
Copy link

Hey @adamkl - is my understanding of your code correct?

  1. You create two services: a navService and a userSessionService by calling helper functions. You then pass these services globally using Context.

  2. These helper functions initialize, start, and return new state machines using xstate. Unfortunately the code for these state machines is not shared.

  3. You write useNavService and useUserSessionService hooks to make it easy to access these services within the app.

@adamkl
Copy link
Author

adamkl commented Feb 11, 2020

Yep, you got it @veeral-patel. That’s exactly what we are doing.
The code in the gist is simplified a bit, and excludes the actual state machine logic (which can be pretty complex), but gives an accurate picture of how we wire everything up and use the machines in our React components.

@veeral-patel
Copy link

@adamkl Got it. What I'm getting from this is that using xstate state in my components is similar to using MobX state in my components.

The value of choosing xstate over MobX likely becomes apparent when writing code to model and manipulate your state.

Could you share a couple of things that xstate makes easy compared to other libraries? What's your favorite thing about using it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment