reactjsreact-hooksreact-custom-hooks

custom hook useEffect issue?


I have 2 custom hooks. one is to handling token (useToken) and another is for just decoding user by token (useUser). Now I want updated token each time in my custom-hook useUser but it never triggers, I have added [token] as dependency but no useEffect call.

import { useState, useEffect } from "react";

function getToken() {
  return window.localStorage.getItem("token");
}
function useToken() {
  const [token, updateToken] = useState(() => getToken());

  const setToken = (newToken) => {
    localStorage.setItem("token", newToken);
    updateToken(newToken);
  };

  return [token, setToken];
}

const getUserFromToken = (token) => {
  if (!token) return null;
  return token + Date().toString();
};
function useUser() {
  const [token] = useToken();
  const [user, setUser] = useState(() => getUserFromToken(token));

  useEffect(() => {
    if (!token) return;

    const newUser = getUserFromToken(token);
    setUser(newUser);
  }, [token]);

  return user;
}

export default function App() {
  const [token, setToken] = useToken();
  const user = useUser();
  const [userName, setUserName] = useState("");

  const handleChange = (e) => setUserName(e.target.value);

  const saveHandler = () => {
    setToken(userName);
  };

  return (
    <div className="App">
      <div>
        <input
          type="text"
          placeholder="Enter Username"
          value={userName}
          onChange={handleChange}
        />
        <button onClick={saveHandler}>Save</button>
      </div>
      <div>User - {user}</div>
    </div>
  );
}

I also tried by using useMemo instead useEffect in useUser hook but none of are working. Whats wrong going here?


Solution

  • Allow me to explain what's happening. Your custom hook useUser is using it's own version of the useToken hook. So when your actual token is updated inside App by calling setToken, it only updates the token inside the App component.

    Notice the hook useUser still has its own version of the token even though you updated the token in App. Custom hooks don't magically talk to each other unless something like useContext is used.

    The fix will be simple. We will pass the App's token to the useUser hook and everything will work.

    function useUser(token) {
      const [user, setUser] = useState(() => getUserFromToken(token));
    
      useEffect(() => {
        if (!token) return;
    
        const newUser = getUserFromToken(token);
        setUser(newUser);
      }, [token]);
    
      return user;
    }
    
    // In App
    const [token, setToken] = useToken();
    const user = useUser(token);
    

    I would however like to offer another piece of advice. I can see the useUser hook doesn't really do much. It basically processes whatever token it gets and spills out a user without performing any actions. For this use case, maintaining a separate state and using useEffect inside useUser seems to be unnecessary. Since user is always derived from the token this seems to a good case to use useMemo. The useUser can thus be simplified as follows:

    function useUser(token) {
      const user = useMemo(() => getUserFromToken(token), [token]);
      return user;
    }
    

    I hope all of this made sense. :)