javascriptreactjsnext.jssmartcontracts

A function inside context does not get executed


I have created a dApp which uses a context folder to communicate with the smart contract, in the context a function called loadAuth which checks if the user is authenticated, then set the account state to user.wallet.address.

context/DataContext.tsx

'use client'

declare let window: any;
import { createContext, useContext, useState } from "react";
import Web3 from "web3";
import { usePrivy } from "@privy-io/react-auth";

interface DataContextProps {
  account: string;
  loading: boolean;
  loadAuth: () => Promise<void>;
};

const DataContext = createContext<DataContextProps>({
  account: "",
  loading: true,
  loadAuth: async () => {},
});

interface Props {
    children?: React.ReactNode;
};

export const DataProvider: React.FC<Props> = ({ children }) => {
  const data = useProviderData();

  return <DataContext.Provider value={data}>{children}</DataContext.Provider>;
};

export const useData = () => useContext<DataContextProps>(DataContext);

export const useProviderData = () => {
  const [loading, setLoading] = useState(true);
  const [account, setAccount] = useState("");
  const {ready, authenticated, user} = usePrivy();

  const loadAuth = async () => {
    if (ready && authenticated && user) {
      const userWallet = user.wallet ? user.wallet.address : "";
      console.log('walletAddress: ', userWallet);
      setAccount(userWallet);
    }
  };

  return {
    account,
    loading,
    loadAuth,
  };
};

The Navbar login users, and upon completion, calls loadAuth to set the account state. The account state is used to check if the user has an address, and then display it.

import { useRouter } from "next/navigation";
import React from "react";
import { useData } from "../contexts/DataContext";

import { useLogin } from "@privy-io/react-auth";

function Navbar() {
  const router = useRouter();
  const { account, loadAuth } = useData();

  const {login} = useLogin({
    onComplete(user, isNewUser, wasAlreadyAuthenticated, loginMethod, loginAccount) {
      loadAuth();
      router.push("/");
    },
  })

  return (
    <>
      <nav className="w-full h-16 mt-auto max-w-5xl">
        <div className="flex flex-row justify-between items-center h-full">
          {account ? (
            <div className="bg-green-500 px-6 py-2 rounded-md cursor-pointer">
              <span className="text-lg text-white">
                {account.substring(0, 10)}...
              </span>
            </div>
          ) : (
            <div
              className="bg-green-500 px-6 py-2 rounded-md cursor-pointer"
              onClick={login}
            >
              <span className="text-lg text-white">Connect</span>
            </div>
          )}
        </div>
      </nav>
    </>
  );
}

export default Navbar;

The issue is loadAuth does not get called properly, and account state never gets assigned the user.wallet.address

Update: app/layout.tsx

import type { Metadata } from "next";
import PrivyProviderWrapper from "../components/privy-provider-wrapper";
import "./globals.css";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Create Next App",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <PrivyProviderWrapper>
          {children}
        </PrivyProviderWrapper>
        </body>
    </html>
  );
}

Solution

  • You are not using DataProvider in your providers tree that's why your loadAuth function is not doing anything.

    Update your layout as follow to use DataProvider.

    src/context/DataContext.tsx

    
    'use client'
    
    declare let window: any;
    import { createContext, useContext, useState } from "react";
    import Web3 from "web3";
    import { usePrivy } from "@privy-io/react-auth";
    
    interface DataContextProps {
      account: string;
      loading: boolean;
      loadAuth: () => Promise<void>;
    };
    
    const DataContext = createContext<DataContextProps>({
      account: "",
      loading: true,
      loadAuth: async () => {},
    });
    
    interface Props {
        children?: React.ReactNode;
    };
    
    export const DataProvider: React.FC<Props> = ({ children }) => {
      const data = useProviderData();
    
      return <DataContext.Provider value={data}>{children}</DataContext.Provider>;
    };
    
    export const useData = () => useContext<DataContextProps>(DataContext);
    
    export const useProviderData = () => {
      const [loading, setLoading] = useState(true);
      const [account, setAccount] = useState("");
      const {ready, authenticated, user} = usePrivy();
    
      const loadAuth = async () => {
        if (ready && authenticated && user) {
          const userWallet = user.wallet ? user.wallet.address : "";
          console.log('walletAddress: ', userWallet);
          setAccount(userWallet);
        }
      };
    
      return {
        account,
        loading,
        loadAuth,
      };
    };
    

    src/app/layout.tsx

    import type { Metadata } from "next";
    import PrivyProviderWrapper from "../components/privy-provider-wrapper";
    import "./globals.css";
    
    // adjust the path according to your folders layout.
    ++ import { DataProvider } from "@/src/src/context/DataContext" 
    
    export const metadata: Metadata = {
      title: "Create Next App",
      description: "Create Next App",
    };
    
    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      return (
        <html lang="en">
          <body>
            <PrivyProviderWrapper>
    ++          <DataProvider>
                {children}
    ++          </DataProvider>
            </PrivyProviderWrapper>
    
            </body>
        </html>
      );
    }