SolidJSS
SolidJSโ€ข3y agoโ€ข
4 replies
JCat ๐ŸŽ„

Using a function outside of components

Hi all, I've come across an interesting situation. I am currently doing a project using SolidJS. I have a hook (hello React) that handles authentication:

import { createSignal } from "solid-js";

export function useAuth() {
  const [isLoaded, setIsLoaded] = createSignal(false);
  const [isAuthed, setIsAuthed] = createSignal(false);

  const signup = async (login, password) => { /* request... */ };

  const login = async (login, password) => {
    try {
      /// axios request...
      setIsAuthed(true);
    } catch (error) {
      // error handling
    }
  };

  const refresh = async () => {
    try {
      // axios request...
      setIsAuthed(true);
    } catch (error) {}
    setIsLoaded(true);
  };

  const logout = async () => {
    try {
      // axios request...
    } catch (error) {}
    setIsAuthed(false);
  };

  return {
    register,
    login,
    refresh,
    logout,
    isAuthed,
    isLoaded,
  };
}


There is also a generic site template that has a check if the user is authorized or not to show the right menu items:

import { useNavigate } from "@solidjs/router";
import { useAuth } from "../hooks";

export default function Main({ children }) {
  const { isAuthed, logout } = useAuth();
  const navigate = useNavigate();

  const doLogout = () => {
    logout();
    navigate("/");
  };

  return (
    <>
      <header className="flex items-center justify-between py-3">
        <a href="/">
          <img src="/logo.png" className="w-12" alt="Logo" />
        </a>
        {isAuthed() ? (
          <nav className="flex items-center gap-4 p-4">
            <a href="/profile">Profile</a>
            <a href="#" onClick={doLogout}>
              Sign out
            </a>
          </nav>
        ) : (
          <nav className="flex items-center gap-4 p-4">
            <a href="/login">Sign in</a>
            <a href="/signup">Sign up</a>
          </nav>
        )}
      </header>

      <main className="h-[calc(100vh-(56px+1.5rem))]">{children}</main>
    </>
  );
}


And also the login page itself which calls the login method:

import { useNavigate } from "@solidjs/router";
import { useAuth } from "../hooks/useAuth";
import { failure } from "../services";

export default function Login() {
  const { login } = useAuth();
  const navigate = useNavigate();

  const submit = async (event) => {
    event.preventDefault();

    // ...
    // A part of the code that is not important for this situation
    // ...

    if (await login(_login, password)) {
      navigate("/");
    }
  };

  return (
    { /* Stylized form with data submission in onSubmit */ }
  );
}


So, the problem is that the state seems to change internally, but is not tracked externally (as if each component in which useAuth() is called has its own state of signals). I initially solved the problem by using createContext/useContext. Later I went to see if there is a state manager library for SolidJS, like Recoil for React. Not finding anything I decided to try to write a wrapper over createContext/useContext similar to Recoil. And what is interesting is that I accidentally noticed unusual behavior of createSingal, and in general all "hooks" in SolidJS. In React all useX functions (useState, useEffect, etc.) should be strictly used only in hooks, which is not the case in SolidJS. I.e. you can write the following code and it will work as it should:

import { createSignal } from "solid-js";

const [isLoaded, setIsLoaded] = createSignal(false);
const [isAuthed, setIsAuthed] = createSignal(false);

export function useAuth() {
    // ...
}


I would like to know if this is considered normal practice in SolidJS, or should I not write this way to avoid bugs in the future?
Was this page helpful?