Gatsby - Uncaught TypeError - Context/build problem - reactjs

I was developing my application in Gatsby and got stuck. Everything is working fine with "gatsby develop", but when I run "gatsby build" I get an error:
"WebpackError: TypeError: Cannot destructure property 'cursorStyles' of 'Object(...)(...)' as it is undefined."
And yeah, cursorStyles are defined in the context, so everything should work perfectly or I am missing something. Tried to clean cache, but still the error occurs, which is very weird cuz didn't have any problems to work on this project locally.
EDIT - yes, I wrapped the gatsby application with the Global Provider as you can see below. I just don't understand why the build doesn't work, when I clearly have the access to the context... ;/
gatsby-browser.js
import React from "react"
import { GlobalProvider } from "./src/context/globalContext"
export const wrapRootElement = ({ element }) => {
return <GlobalProvider>{element}</GlobalProvider>
}
Context - values are defined in global provider const
import React, { createContext, useReducer, useContext } from "react"
//Define Context
const GlobalStateContext = createContext()
const GlobalDispatchContext = createContext()
//Reducer
const globalReducer = (state, action) => {
switch (action.type) {
case "TOGGLE_THEME": {
return {
...state,
currentTheme: action.theme,
}
}
case "CURSOR_TYPE": {
return {
...state,
cursorType: action.cursorType,
}
}
default: {
throw new Error(`Unhandled action type: ${action.type}`)
}
}
}
export const GlobalProvider = ({ children }) => {
const [state, dispatch] = useReducer(globalReducer, {
currentTheme: "dark",
cursorType: false,
cursorStyles: ["pointer", "hovered", "locked", "white"],
})
return (
<GlobalDispatchContext.Provider value={dispatch}>
<GlobalStateContext.Provider value={state}>
{children}
</GlobalStateContext.Provider>
</GlobalDispatchContext.Provider>
)
}
//custom hooks for when we want to use our global state
export const useGlobalStateContext = () => useContext(GlobalStateContext)
export const useGlobalDispatchContext = () => useContext(GlobalDispatchContext)
Layout.js - problem occurs when destructuring and using my custom hook
import React, {useState} from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import { createGlobalStyle, ThemeProvider } from "styled-components"
import { normalize } from "styled-normalize"
import Header from './header'
import Cursor from './customCursor'
import Navigation from './navigation'
import { useGlobalStateContext, useGlobalDispatchContext } from '../context/globalContext'
const Layout = ({ children }) => {
const { cursorStyles, currentTheme } = useGlobalStateContext()
const dispatch = useGlobalDispatchContext()
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
const darkTheme = {
background: '#000',
text:'#fff',
red: '#ea291e'
}
const lightTheme = {
background: '#fff',
text:'#000',
red: '#ea291e'
}
const onCursor = cursorType => {
cursorType = (cursorStyles.includes(cursorType) && cursorType || false)
dispatch({ type: "CURSOR_TYPE", cursorType: cursorType})
}
const [toggleMenu, setToggleMenu] = useState(false)
return(
<ThemeProvider theme={currentTheme === "dark" ? darkTheme : lightTheme}>
<GlobalStyle/>
<Cursor toggleMenu={toggleMenu} />
<Header onCursor={onCursor} toggleMenu={toggleMenu} setToggleMenu={setToggleMenu} />
<Navigation onCursor={onCursor} toggleMenu={toggleMenu} setToggleMenu={setToggleMenu} />
<main>{children}</main>
{console.log(currentTheme)}
</ThemeProvider>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout

Finally I solved it! I don't know why, but I guess it's just Gatsby who works in a mysterious way. I duplicated my code from gatsby-browser, and put exactly the same thing into the gatsby-ssr file and... surprisingly the weird errors about "object undefined" disappeared.
Unfortunately, due to the fact that gatsby build in that case works similar to the next.js' server-side rendering, I had to fix my code in different places - e.g "window" properties had to be rendered conditionally - but duplicating gatsby-browser's code to the gatsby-ssr fixed the building problem! So as I thought, it wasn't a problem with my code, but rather a problem with gatsby's config.

Very strange. This problem happens only in production. Duplicating the code from gatsby-browser inside gatsby-ssr - really solves it!
import React from 'react';
import CartProvider from './src/components/CartProvider';
export const wrapRootElement = ({ element }) => (
<CartProvider>{element}</CartProvider>
);

Related

TypeError: supabaseClient.auth.getSession is not a function. Error with NextJS and Supabse

I'm using supabase auth helper package to React and Next in my project. Immediately, when I start the page, I get an error message that says the following:
And it is that I am not calling that supabaseClient.auth.getSession() method. Which makes me believe that the error is internal. i'm just following the documentation: doc. And use the code that appears in the Basic Setup and in Server-side rendering (SSR)
Actually i have this:
`
// pages/_app.js
import { useState } from 'react'
import { useRouter } from 'next/router'
import '../styles/global.css'
import ProfileContext from '../utils/context/ProfileContext'
import useProfile from '../utils/hooks/useProfile'
import ProtectedRoutes from '../utils/constants/rutas/protectedRoutes'
const MyApp = ({ Component, pageProps }) => {
const [supabaseClient] = useState(() => createBrowserSupabaseClient())
console.log('supabaseClient', supabaseClient)
return (
<SessionContextProvider
supabaseClient={supabaseClient}
initialSession={pageProps.initialSession}
>
<ProfileContext.Provider value={useProfile()}>
<Component {...pageProps} />
</ProfileContext.Provider>
</SessionContextProvider>
)
}
export default MyApp
`
`
// pages/index.js
import { createServerSupabaseClient } from '#supabase/auth-helpers-nextjs'
import { useState, useEffect } from "react"
import Layout from "../layout/Layout"
import List from "../components/List"
import Empty from "../components/Empty"
import { supabase } from "../utils/supabaseClient"
const Index = ({ allData }) => {
const [session, setSession] = useState(null)
useEffect(() => {
setSession(supabase.auth.session())
}, [])
return (
<Layout>
{allData.length ? <List allData={allData} /> : <Empty />}
</Layout>
);
}
export default Index
export async function getServerSideProps(ctx) {
let { data: receptions, error } = await supabase
.from('receptions')
.select('*')
.order('id', { ascending: false })
if (error) throw error
return {
props: {
allData: receptions
},
};
}
`
As you will notice at no time I make use of the getSession() method. I hope I explained myself well.
Try this in to see if the user is not logged in to send the user to login.
This would be because you have an older version of the supabase-js library installed. Please install the latest 2.x version in order for this to work.
Run the following command to get the latest v2 version
npm install #supabase/supabase-js#2

React native typescript: usecontext functions not firing from inside child component

I have an issue when i try to use functions from a context inside a child component in a React native android app.
Below is my code for the context, and the form component im using it in (stripped down for brevity).
The "isFormOpen" object can be read no problem from inside any children that is wrapped in the provider, but when i try to call the "toggleForm" function from the same child component, it does nothing, no console errors either.
I have another context which is identical in structure and syntax except for vairable and function names etc, and that works perfectly, so im a bit confused as to why this does not work. I removed the other context, thinking there might be some type of conflict, but didnt solve it.
AccountContext.tsx
import React, { FC, createContext, useContext, useState } from 'react';
interface AccountContextType {
isFormOpen: boolean,
toggleForm: (toggle: boolean) => void
};
export const AccountContext = createContext<AccountContextType>({
isFormOpen: false,
toggleForm: () => null
});
export const AccountContextProvider: FC = props => {
const [formOpen, setFormOpen] = useState<boolean>(false);
const toggleForm = (toggle: boolean) => {
setFormOpen(toggle);
}
const value: AccountContextType = {
isFormOpen: formOpen,
toggleForm
}
return (
<AccountContext.Provider value={value}>
{props.children}
</AccountContext.Provider>
)
}
export const useAccountContext = () => useContext(AccountContext);
TrackUploadForm.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import { Button } from 'react-native-paper';
import { useAccountContext } from '../contexts/AccountContext';
import { AccountContextProvider } from '../contexts/AccountContext';
const TrackUploadForm = () => {
const accountContext = useAccountContext();
return (
<AccountContextProvider>
<SafeAreaView>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</SafeAreaView>
</AccountContextProvider>
)
};
export default TrackUploadForm;
useAccountContext is called outside the provider
export default function App() {
return (
<AccountContextProvider>
<Content />
</AccountContextProvider>
);
}
const Content = () => {
const accountContext = useAccountContext();
return (
<div className="App">
<h1>{accountContext.isFormOpen ? "true" : "false"}</h1>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</div>
);
};
accountContext.toggleForm(false) <-- always false, change it to accountContext.toggleForm(!accountContext.isFormOpen)
Together we have
https://codesandbox.io/s/cranky-panini-yo129

How to test code that uses a custom hook based on useContext with react-testing-library and jest

I've created a custom context hook - and I'm struggling to figure out how to pass values to its provider during testing.
My hook:
import React, { createContext, useContext, useState } from 'react';
const Context = createContext({});
export const ConfigurationProvider = ({ children }) => {
// Use State to keep the values
const [configuration, setConfiguration] = useState({});
// pass the value in provider and return
return (
<Context.Provider
value={{
configuration,
setConfiguration,
}}
>
{children}
</Context.Provider>
);
};
export const useConfigurationContext = () => useContext(Context);
export const { Consumer: ConfigurationConsumer } = Context;
This is how it's used in the application:
function App() {
return (
<ConfigurationProvider>
<div className="app">
<ComponentA />
</div>
</ConfigurationProvider>
);
}
And in ComponentA:
const ComponentA = () => {
// Get configuration
const configuration = useConfigurationContext();
return (
<div>{JSON.stringify(configuration)}</div>
)
}
This all works fine - considered that I'm calling setConfiguration from another component and set an object. Now for the testing part:
import React, { Component, createContext } from 'react';
import { render, waitFor } from '#testing-library/react';
import ComponentA from 'componentA';
const config = {
propertyA: 'hello',
};
test('renders the config', async () => {
const ConfigurationContext = createContext();
const { queryByText } = render(
<ConfigurationContext.Provider value={config}>
<ComponentA />
</ConfigurationContext.Provider>
);
expect(queryByText('hello')).toBeInTheDocument();
});
This doesn't work - I'm expecting the value that I'm sending in would be rendered in the div, but the context is an empty object. What am I doing wrong?
Thanks to Carle B. Navy I got the reason why it doesn't work. For other people two wonder what the solution is I fixed it by doing the following:
In my context hook, I changed the last line to export the provider as well:
export const { Consumer: ConfigConsumer, Provider: ConfigProvider } = Context;
Then in my test case, instead of creating a new context, I import the ConfigProvider at the top, and then:
const { queryByText } = render(
<ConfigProvider value={config}>
<ComponentA />
</ConfigProvider>
);
Thanks for helping me solve this and hope this helps someone else.

GatsbyJs update provider value from onRouteUpdate

I'm using a provider for holding global location using gatsby's browser API for accessing location by consumer in other components. The problem is i can't change the global location from onRouteUpdate, Here is my code:
gatsby-browser.js:
import React, {useContext} from 'react';
import Provider,{ appContext } from './provider';
export const onRouteUpdate = ({ location, prevLocation }) => {
console.log('new pathname', location.pathname)
console.log('old pathname', prevLocation ? prevLocation.pathname : null)
// wanna set the new location for provider to use in other pages
// this code does not work
return(
<appContext.Consumer>
{context => {
context.changeLocation(location.pathname)
}})
</appContext.Consumer>
)
}
Provider.js:
import React, { useState } from 'react';
import { globalHistory as history } from '#reach/router'
export const appContext = React.createContext();
const Provider = props => {
const [location, setLocation] = useState(history.location);
return (
<appContext.Provider value={{
location,
changeLocation: (newLocation)=> {setLocation({location:newLocation}); console.log('changing')}
}}>
{props.children}
</appContext.Provider>
)
};
export default Provider;
Thanks.
onRouteUpdate isn’t expected to return React nodes, and so the React element you’re returning isn't going to be evaluated like you’d expect.
Since you’re only looking to store the current page, you don’t actually need to do anything manually onRouteUpdate because this functionality is available out-of-the-box with Gatsby.
// gatsby-browser.js
import React from "react"
import { appContext } from "src/provider"
export const wrapPageElement = ({ element, props }) => (
<AppContext.Provider value={{ location: props.location }}>
{React.createElement(element, props)}
</AppContext.Provider>
)

How to fix setState not updating in react context

I am running into an issue where my context is not getting updated when I try to update it through a react hook. I have posted the code below and feel like I may be missing one slight blip.
appContext.tsx
import React, { useState } from "react"
import { ICreateContext } from "../types/generics"
export interface IAppProviderState {
isNightMode: boolean
}
const defaults = { isNightMode: false }
const AppContext = React.createContext<ICreateContext<IAppProviderState>>([
defaults,
() => null,
])
const AppProvider = (props: { children?: JSX.Element }) => {
const [state, setState] = useState<IAppProviderState>({
isNightMode: defaults.isNightMode,
})
return (
<AppContext.Provider value={[state, setState]}>
{props.children}
</AppContext.Provider>
)
}
export { AppContext, AppProvider }
appStore.tsx
import { useContext } from "react"
import { AppContext, IAppProviderState } from "../contexts/appContext"
function useAppStore() {
const [state, setState] = useContext(AppContext)
function toggleNightMode(bool: boolean) {
setState({
isNightMode: bool,
})
}
return {
toggleNightMode,
state,
}
}
export { useAppStore }
// somewhere in the view I use the useAppStore hook.
// but using the method toggleNightMode, even though fires off on
// appStore.tsx - is not updating the root context state.
const { state, toggleNightMode } = useAppStore()
//..e.g
toggleNightMode(true);
I have removed the context and checked if the hook works with local state and it did update, sadly this is not the result I want.
Any help would be much appreciated.
Thanks.
Not sure if this will help but I've created a version of what you are trying to do (not in Typescript sorry) which just toggles the value of isNightMode from true to false with a button click.
I have moved the Context and Provider code into separate files but that is just how I prefer my code.
https://codesandbox.io/s/wizardly-elbakyan-i6umw

Resources