Rom-888
Rom-888

Reputation: 876

Next.js useContext TypeError: Cannot destructure 'Object(...)(...)' as it is undefined

I try to change the state of my cursor in a Next.js app with useContext but got this error :

TypeError: Cannot destructure 'Object(...)(...)' as it is undefined.

The state should change to isActive: true when hover the button

My ContextProvider :

import { createContext, useState } from "react";

export const CursorContext = createContext();

const CursorContextProvider = ({ children }) => {
  const [cursor, setCursor] = useState({ active: false });
  return (
    <CursorContext.Provider value={[cursor, setCursor]}>
      {children}
    </CursorContext.Provider>
  );
};

export default CursorContextProvider;

in the App :

import App from "next/app";
import Head from "next/head";
import Layout from "../components/Layout";
import CursorContextProvider from "../components/CursorContextProvider";
import Cursor from "../components/Cursor";
import { getCategories } from "../utils/api";
import "../styles/index.css";
import "../styles/style.scss";

const MyApp = ({ Component, pageProps, children }) => {
  return (
    <Layout categories={pageProps.categories}>
      <Head>
        <link rel="preconnect" href="https://app.snipcart.com" />
        <link rel="preconnect" href="https://cdn.snipcart.com" />
        <link
          rel="stylesheet"
          href="https://cdn.snipcart.com/themes/v3.0.16/default/snipcart.css"
        />
        <script src="https://cdn.snipcart.com/themes/v3.0.16/default/snipcart.js" />
      </Head>
      <CursorContextProvider>
      <Cursor />
      {children}
    </CursorContextProvider> 
      <Component {...pageProps} />
    </Layout>
  );
};

// getInitialProps disables automatic static optimization for pages that don't
// have getStaticProps. So [[...slug]] pages still get SSG.
// Hopefully we can replace this with getStaticProps once this issue is fixed:
// https://github.com/vercel/next.js/discussions/10949
MyApp.getInitialProps = async (ctx) => {
  // Calls page's `getInitialProps` and fills `appProps.pageProps`
  const appProps = await App.getInitialProps(ctx);
  // Fetch global site settings from Strapi
  const categories = await getCategories();
  // Pass the data to our page via props
  return { ...appProps, pageProps: { categories, path: ctx.pathname } };
};

export default MyApp;

Here my Cursor component :

import React, { useContext } from "react";
import useMousePosition from "./useMousePosition";
import { CursorContext } from "./CursorContextProvider";

const Cursor = () => {
  const { clientX, clientY } = useMousePosition();
  const [cursor] = useContext(CursorContext);
  return (
    <div 
      style={{ 
        position: "fixed",
        top: 0,
        bottom: 0,
        left: 0,
        right: 0,
        zIndex: 9999,
        pointerEvents: "none"
      }}
    >
      <svg
        width={50}
        height={50}
        viewBox="0 0 50 50"
        style={{
          position: "absolute",
          left: clientX,
          top: clientY,
          transform: `translate(-50%, -50%) scale(${cursor.active ? 2.5 : 1})`,
          stroke: cursor.active ? "black" : "white",
          strokeWidth: 1,
          fill: cursor.active ? "rgba(255,255,255,.5)" : "black",
          transition: "transform .2s ease-in-out",
        }}
      >
        <circle
          cx="25"
          cy="25"
          r="8"
        />
      </svg>
    </div>
  );
};
export default Cursor;

And a test Button component :

import { useContext, useCallback } from "react";
import { CursorContext } from "./CursorContextProvider";
const Button = () => {
  const [, setCursor] = useContext(CursorContext);

  const toggleCursor = useCallback(() => {
    setCursor(({ active }) => ({ active: !active }));
  });
  return (
    <button
      type="button"
      style={{ padding: "1rem" }}
      onMouseEnter={toggleCursor}
      onMouseLeave={toggleCursor}
    >
      HOVER ME
    </button>
  );
};
export default Button;

Do you see where I did something wrong to make the setState undefined ?

Upvotes: 1

Views: 2638

Answers (1)

juliomalves
juliomalves

Reputation: 50278

This is most likely due to your CursorContextProvider not wrapping the component where Button is being used. Try moving it to the top of your returned JSX in App.

const MyApp = ({ Component, pageProps, children }) => {
    return (
        <CursorContextProvider>
            <Layout categories={pageProps.categories}>
                //...
            </Layout>
        </CursorContextProvider> 
    );
};

Upvotes: 1

Related Questions