#[SPA Routing]: How to not call data function for the protected routes

1 messages · Page 1 of 1 (latest)

slow belfry
#

Let's imagine we have the following app

const App = () => (
  <AuthService>
    <Router>
      <Routes>
        <Route path="/" data={layoutDataFunction} component={LazyPageLayout}>
          <Route path="login" component={LazyLoginPage} />
          <Route path="" component={LazyDefaultLayout}>
            <Route path="content" component={LazyContentPage} data={contentPageDataFunction} />
            {/* Other pages */}
          </Route>
        </Route>
      </Routes>
    </Router>
  </AuthService>
)
```;

I handle all auth in the layout data function:
```tsx
export const layoutDataFunction = ({ navigate, location }: RouteDataFuncArgs) => {
  const { isLogged } = useContext(AuthContext);

  createComputed(() => {
    if (isLogged()) {
      if (location.pathname === "/login") navigate(PagePath.Channel);
    } else {
      if (location.pathname !== "/login") navigate(PagePath.Login);
    }
  });
};

When I enter /content page (unlogin state) the redirect hasn't worked yet, so contentPageDataFunction still fetches the data. I want to not load extra data if I don't need it. How to properly cancel the request for the content page in this case?

Imagine we have the following contentPageDataFunction function:

export const contentPageDataFunction = ({data}) => {
  // I know that here we could get logged state from the parent
  // data function and then inside a resource make this check.
  // However in this case I'll need to change TS types, because
  // it will return <User | null> (if I return null for unlogged state)

  // How to deal with that???
  const [user] = createResource(async () => {
    const result = await myVideosAPI.getMyVideos();

    return result;
  });

  return user;
};

Is there some RIGHT way to do protect routing for SPA apps?

#

I don't want to pass the logged state for all data functions which require auth check, because I want to keep the auth logic in one place.

#
export const contentPageDataFunction = ({data}) => {
  const {isLoggedSignal} = data;
  const [user] = createResource(isLoggedSignal, async (isLogged) => {
    // if I do smth like this
    // then I immediately change the state of the resource
    // which will affect suspense (the state will instantly be 'ready')
    // and TS handling stuff of course...
    if (!isLogged) return null;

    const result = await myVideosAPI.getMyVideos();

    return result;
  });

  return user;
};
slow belfry
#

[SPA Routing]: How to not call data function for the protected routes

slow belfry
#

EXAMPLE APP 1/2:

import { Navigate, Outlet, Route, RouteDataFuncArgs, Router, Routes, useRouteData } from "@solidjs/router";
import { ParentComponent, createComputed, createContext, createResource, createSignal, useContext } from "solid-js";

const AuthContext = createContext();

export const AuthService: ParentComponent = (props) => {
  const [isLogged, setLogged] = createSignal(false);

  return (
    <AuthContext.Provider
      value={{
        isLogged,
        logout: () => setLogged(false),
        login: () => setLogged(true),
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export const layoutDataFunction = ({ navigate, location }: RouteDataFuncArgs) => {
  console.log("[DF]: layout");
  const { isLogged } = useContext(AuthContext)!;

  createComputed(() => {
    if (isLogged()) {
      if (location.pathname === "/login") {
        console.log("Navigate to /content", isLogged());
        navigate("/content");
      }
    } else {
      if (location.pathname !== "/login") {
        console.log("Navigate to /login", isLogged());
        navigate("/login");
      }
    }
  });
};

export const channelPageDataFunction = () => {
  console.log("[DF]: content");
  const [data] = createResource(async () => {
    console.log("THIS thing is called despite the fact that navigate calls earlier");

    await new Promise((resolve) =>
      setTimeout(() => {
        resolve;
      }, 2000),
    );

    return "data!";
  });

  return data;
};
#

EXAMPLE APP: 2/2

export const App = () => (
  <AuthService>
    <Router>
      <Routes>
        <Route
          path="/"
          data={layoutDataFunction}
          component={() => (
            <div>
              Layout
              <Outlet />
            </div>
          )}
        >
          <Route
            path="login"
            component={() => (
              <div>
                <h1>LOGIN PAGE</h1>
                <button type="button" onClick={useContext(AuthContext).login}>
                  Login
                </button>
              </div>
            )}
          />
          <Route path="">
            <Route
              path="content"
              component={() => {
                const data = useRouteData();

                return (
                  <div>
                    <h1>CONTENT PAGE</h1>
                    <button type="button" onClick={useContext(AuthContext).logout}>
                      logout
                    </button>
                  </div>
                );
              }}
              data={channelPageDataFunction}
            />
            {/** Other pages */}
          </Route>
          <Route path="*" element={<Navigate href="/content" />} />
        </Route>
      </Routes>
    </Router>
  </AuthService>
);
#

When I open '/content' page in the browser, I see this:

#

Here you can see that navigate runs before content DF, however DF is still running! Is it a bug?

vocal juniper
#

I haven't read all this yet, but <Route path="" seems invalid. From a readability perspective idk how to interpret it.

slow belfry
#

It's valid. in my app I use this route for common layout, but I omitted it here.