最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - is there a way to persist sensitive user data across page refresh in react? - Stack Overflow

programmeradmin0浏览0评论

i have created a userProvider component that fetches the user from the server on page load. Navigating between pages do not trigger a re-render, but when i refresh the page the user is fetched again. I want user data to persist across page-refresh.

important: user object contains sensitive information, such as email, historyEnabled, and role. As such I do not want to store data in local storage, as I have no way of verifying it. (I know i can setup an api endpoint for that, but that defeats the purpose of reducing load times)

here is the userProvider.tsx

"use client";
import React, { useContext, useEffect } from "react";
import { createContext, useState } from "react";

interface userProps {
  id: string;
  name: string;
  about?: string;
  email: string;
  role: string;
  historyEnabled: boolean;
}
interface userContextProps {
  user: userProps | undefined;
  isLoaded: boolean;
  refreshUser: (user?: userProps) => void;
}
//context
const userContext = createContext<userContextProps>({
  user: undefined,
  isLoaded: false,
  refreshUser: () => {},
});

//context provider component
const UserProvider = ({ children }: { children: React.ReactNode }) => {
  //user State
  const [user, setUser] = useState<userProps | undefined>();
  const [isLoaded, setIsLoaded] = useState(false);

  //get user from server on page load/reload
  useEffect(() => {
    fetchUser();
  }, []);

  //fetchUser Function
  const fetchUser = async () => {
    try {
      setIsLoaded(false);
      const res = await fetch("/api/user");
      if (!res.ok) {
        setUser(undefined);
        throw new Error(`Error ${res.status} failed to fetch user`);
      }
      const user = await res.json();
      setUser(user);
      console.log("fetched user: ", user);
    } catch (error) {
      setUser(undefined);
      console.log(error);
    } finally {
      setIsLoaded(true);
    }
  };

  //function to update user state
  const refreshUser = async (user?: userProps) => {
    if (user) {
      setUser(user);
      console.log("user: ", user);
    } else {
      await fetchUser();
    }
  };

  return (
    <userContext.Provider value={{ user, refreshUser, isLoaded }}>
      {children}
    </userContext.Provider>
  );
};

const useUser = () => {
  const context = useContext(userContext);
  if (!context.user) {
    throw new Error("useUser must be used within a UserProvider");
  }
  return context;
};

export { useUser, UserProvider };

here is how i use it in nextjs root layout:

  import type { Metadata } from "next";
  import "@/app/globals.css";
  import { montserrat } from "@/app/ui/fonts";
  import Dashboard from "@/app/(components)/dashboard/index";
  import { Toaster } from "./(components)/reusable-ui/toaster";
  import { UserProvider } from "./providers/UserProvider";
  export const metadata: Metadata = {
    title: "forage",
    description: "forage is a blog site",
  };

  export default function RootLayout({
    children,
  }: Readonly<{
    children: React.ReactNode;
  }>) {
    return (
      <html lang="en">
        <body
          className={`${montserrat.className}  antialiased bg-item lg:bg-background`}
        >
          <UserProvider>
            <Dashboard />
            {children}
          </UserProvider>
          <Toaster />
        </body>
      </html>
    );
  }

my questions are:

  1. can i use useMemo to memoize the user data fetch request? does it persist data when I refresh my page?
  2. what is the common pattern for this problem? how does Auth0 or clerk handle this issue? I know clerk has const {user, isLoading} = useUser(); and I have tried to replicate it in my code.

I would be glad if any of my questions can be answered, thanks!

i have created a userProvider component that fetches the user from the server on page load. Navigating between pages do not trigger a re-render, but when i refresh the page the user is fetched again. I want user data to persist across page-refresh.

important: user object contains sensitive information, such as email, historyEnabled, and role. As such I do not want to store data in local storage, as I have no way of verifying it. (I know i can setup an api endpoint for that, but that defeats the purpose of reducing load times)

here is the userProvider.tsx

"use client";
import React, { useContext, useEffect } from "react";
import { createContext, useState } from "react";

interface userProps {
  id: string;
  name: string;
  about?: string;
  email: string;
  role: string;
  historyEnabled: boolean;
}
interface userContextProps {
  user: userProps | undefined;
  isLoaded: boolean;
  refreshUser: (user?: userProps) => void;
}
//context
const userContext = createContext<userContextProps>({
  user: undefined,
  isLoaded: false,
  refreshUser: () => {},
});

//context provider component
const UserProvider = ({ children }: { children: React.ReactNode }) => {
  //user State
  const [user, setUser] = useState<userProps | undefined>();
  const [isLoaded, setIsLoaded] = useState(false);

  //get user from server on page load/reload
  useEffect(() => {
    fetchUser();
  }, []);

  //fetchUser Function
  const fetchUser = async () => {
    try {
      setIsLoaded(false);
      const res = await fetch("/api/user");
      if (!res.ok) {
        setUser(undefined);
        throw new Error(`Error ${res.status} failed to fetch user`);
      }
      const user = await res.json();
      setUser(user);
      console.log("fetched user: ", user);
    } catch (error) {
      setUser(undefined);
      console.log(error);
    } finally {
      setIsLoaded(true);
    }
  };

  //function to update user state
  const refreshUser = async (user?: userProps) => {
    if (user) {
      setUser(user);
      console.log("user: ", user);
    } else {
      await fetchUser();
    }
  };

  return (
    <userContext.Provider value={{ user, refreshUser, isLoaded }}>
      {children}
    </userContext.Provider>
  );
};

const useUser = () => {
  const context = useContext(userContext);
  if (!context.user) {
    throw new Error("useUser must be used within a UserProvider");
  }
  return context;
};

export { useUser, UserProvider };

here is how i use it in nextjs root layout:

  import type { Metadata } from "next";
  import "@/app/globals.css";
  import { montserrat } from "@/app/ui/fonts";
  import Dashboard from "@/app/(components)/dashboard/index";
  import { Toaster } from "./(components)/reusable-ui/toaster";
  import { UserProvider } from "./providers/UserProvider";
  export const metadata: Metadata = {
    title: "forage",
    description: "forage is a blog site",
  };

  export default function RootLayout({
    children,
  }: Readonly<{
    children: React.ReactNode;
  }>) {
    return (
      <html lang="en">
        <body
          className={`${montserrat.className}  antialiased bg-item lg:bg-background`}
        >
          <UserProvider>
            <Dashboard />
            {children}
          </UserProvider>
          <Toaster />
        </body>
      </html>
    );
  }

my questions are:

  1. can i use useMemo to memoize the user data fetch request? does it persist data when I refresh my page?
  2. what is the common pattern for this problem? how does Auth0 or clerk handle this issue? I know clerk has const {user, isLoading} = useUser(); and I have tried to replicate it in my code.

I would be glad if any of my questions can be answered, thanks!

Share Improve this question edited Feb 4 at 12:15 Zheng Jiawen asked Feb 4 at 12:09 Zheng JiawenZheng Jiawen 133 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

useMemo does not keep your data on page refresh (full reload browser page and not re-render). Did you try leveraging caching for your API response?

发布评论

评论列表(0)

  1. 暂无评论