【问题标题】:Next.js TypeError although component not renderedNext.js TypeError 尽管组件未呈现
【发布时间】:2021-12-22 06:34:42
【问题描述】:

useSession((s) => s.user) 将返回用户 objectnull。在索引页面上应该显示用户名。该页面包含在PrivatePageProvider 中,如果用户对象为空,它将重定向客户端。因此,如果用户为 null,则甚至不应呈现 IndexPage,而是提供程序返回 null 并重定向。然而,这不起作用并且TypeError 被抛出,因为IndexPage 尝试访问nullname。使用 react-router 这种方法有效,但不适用于 Next。我知道我可以只使用可选链接运算符,但我想假设如果页面包含在 PrivatePageProvider 中,user 不为空。

function PrivatePageProvider({ children }) {
    const user = useSession((s) => s.user);
    const router = useRouter();

    useEffect(() => {
        if (!user) {
            router.push('/auth/login');
        }
    }, [user, router]);

    return !user ? null : children;
}
function IndexPage() {
    const user = useSession((s) => s.user);

    return (
        <PrivatePageProvider>
            <h1>Hello {user.name}</h1>
        </PrivatePageProvider>
    );
}

错误:

TypeError: Cannot read properties of null (reading 'name')

为什么即使IndexPage 没有被渲染,也会出现这个错误?如何在不涉及服务器端的情况下让我的方法工作?

【问题讨论】:

    标签: reactjs authentication next.js


    【解决方案1】:

    为什么即使 IndexPage 没有被渲染,这个错误也会出现? > 因为它正在破坏,你有一个错误,它不会渲染。

    如何在不涉及服务器端的情况下让我的方法发挥作用? > 将您的代码包装在useEffect 中。 useEffect 只会在客户端执行,所以任何在服务器端渲染的东西都会忽略效果并等到它被渲染后再执行。这是因为任何效果都只会在 DOM 被渲染后执行。如果user 在服务器上为空,它将在user.name 上中断,除非您将其移至效果。

    此外,我对你到底想要做什么感到很困惑,为什么子组件中有一个useEffect,而不是你应该在IndexPage 中这样做。我也不确定PrivatePageProvider 的用途是什么,如果页面提供程序呈现索引页面会更有意义,反之亦然。您也不应该将用户作为孩子传递,而是传递数据道具并在子页面中呈现用户,然后您不需要再次获取用户。

    话虽如此,看着您的代码,在我看来,您似乎在尝试做某事,然后由于不小心将您的想法与您使用页面和页面提供程序的方式交换了而感到困惑/迷失.也许这就是你想要的:

    const PrivatePageProvider = ({children}) => {
      const user = useSession((s) => s.user)
      const router = useRouter()
    
      useEffect(() => {
        if (!user) {
          router.push('/auth/login')
        }
      }, [user])
    
      return !user ? <></> : children
    }
    
    const IndexPage = () => {
      const user = useSession((s) => s.user)
    
      return <h1>Hello {user.name}</h1>
    }
    

    这样你就可以像这样使用它:

    <PrivatePageProvider>
      <IndexPage/>
    </PrivatePageProvider>
    
    <PrivatePageProvider>
      <ExamplePage/>
    </PrivatePageProvider>
    

    【讨论】:

      【解决方案2】:

      我认为这种方法也不适用于 react-router。该错误与javascript在执行代码时知道的上下文有关。为了更好地理解,您正在渲染的 JSX 树转换为纯 js 如下所示:

      function IndexPage() {
          const user = useSession((s) => s.user);
          return (React.createElement(PrivatePageProvider, null,
              React.createElement("h1", null,
                  "Hello ",
                  user.name)));
      }
      

      当 javascript 执行一个函数时,它首先计算参数;在这种情况下,当它看到其中一个试图访问一个空对象(user.name)上的属性时,它甚至不调用函数就抛出 TypeError。

      如果将整个用户对象作为道具传递给另一个元素,情况就不同了

      function IndexPage() {
        const user = useSession((s) => s.user);
        return (
          <PrivatePageProvider>
            <SomeComponent user={user} />
          </PrivatePageProvider>
        );
      }
      

      它转译为

      function IndexPage() {
          const user = useSession((s) => s.user);
          return (React.createElement(PrivatePageProvider, null,
              React.createElement(SomeComponent, { user: user })));
      }
      

      这里的javascript不知道你会在SomeComponent中访问user.name,因此它会继续执行而不会出错

      我建议使用 Context API 为包装在 PrivatePageProvider 中的组件提供非空用户

      const UserContext = React.createContext({});
      
      const useUser = () => {
        const { user } = useContext(UserContext);
        return user;
      };
      
      function PrivatePageProvider({ children }: any) {
        const user = useSession((s) => s.user);
        ...
      
        return !user ? null : (
          <UserContext.Provider value={{ user }}>{children}</UserContext.Provider>
        );
      }
      
      function SomeComponent() {
        const user = useUser();
        return <h1>{user.name}</h1>;
      }
      
      function IndexPage() {
        return (
          <PrivatePageProvider>
            <SomeComponent />
          </PrivatePageProvider>
        );
      }
      

      【讨论】:

        【解决方案3】:

        我的项目也有类似的问题。 我将nextJSnext-auth 一起使用,next-auth 提供了loading 状态。我不知道您如何获取用户数据,但除了user 之外,您还应该获取loading 状态并使用loading 处理身份验证。像这样:

        function PrivatePageProvider({ children }) {
            const [session, loading] = useSession();
            const router = useRouter();
        
        
            const isAuthenticate = !!user;
        
            useEffect(() => {
                if (loading) return; // Do nothing while loading
        
                if (!isAuthenticate) {
                    router.push('/auth/login'); // If not authenticated, force log in
                }
            }, [user, loading]);
        
            if(isAuthenticate) {
                return <>children</>
            }
        
            // Session is being fetched, or no user.
            // If no user, useEffect() will redirect.
            return <span>... loading</span>;
        }
        

        我把next-auth包地址,如果你喜欢使用它: next-auth

        【讨论】:

          猜你喜欢
          • 2021-10-25
          • 2021-05-05
          • 2021-05-22
          • 2019-06-20
          • 2021-03-19
          • 2014-08-12
          • 1970-01-01
          • 1970-01-01
          • 2021-09-28
          相关资源
          最近更新 更多