React production build failing with useContext and keys - javascript

Hi I am using gatsby and the build seems to be failing in this useContext file:
Gatsby build
Building static HTML failed for path "/components/DarkThemeContext/DarkThemeContext/"
DarkThemeContext.js
import React, { useState, useContext } from "react"
const DarkThemeContext = React.createContext({
darkMode: false,
setDarkMode: () => {},
})
export const DarkThemeProvider = ({ children }) => {
const [isDark, setIsDark] = useState(false)
return (
<DarkThemeContext.Provider
value={{
darkMode: isDark,
setDarkMode: setIsDark,
}}
>
{children}
</DarkThemeContext.Provider>
)
}
const useDarkThemeContext = () => useContext(DarkThemeContext);
export default useDarkThemeContext;
It fails in this file during build but the exact error it spits out is:
Objects are not valid as a React child (found: object with keys {darkMode, setDarkMode}). If you meant to render a collection of children, use an array instead.
I am not sure how and why this is it picking this up as children
Edit: included code for DarkThemeProvider render
gatsby-browser.js
import React from "react"
import { DarkThemeProvider } from './src/pages/components/DarkThemeContext/DarkThemeContext'
export const wrapRootElement = ({ element }) => {
return <DarkThemeProvider>{element}</DarkThemeProvider>
}
Edit: include code where context is accessed for darkMode
navDrawer.jsx
const { darkMode } = useDarkThemeContext()
const darkModeStyling = createMuiTheme({
palette: {
type: darkMode ? "dark" : "light",
},
})
...
<ThemeProvider theme={darkModeStyling}>
...
</ThemeProvider>

Related

Getting "Objects are not valid as a React child" when using Gatsby wrapRootElement TypeScript

I am trying to implement a theme provider in Gatsby using the wrapRootElement browser API. I've been looking at examples online and can't find where I went wrong. I am getting an error "Objects are not valid as a React child (found: object with keys {children})."
This is the first time I'm using Gatsby browser API, I know the problem is with the children I'm trying to pass down, with the element being an object, but looking at all the examples I can find online they are implemented the same way.
gatsby-browser.js
import React from "react"
import ThemeWrapper from './src/components/theme/theme'
export function wrapRootElement({ element }) {
return <ThemeWrapper>{element}</ThemeWrapper>
}
theme.tsx
import * as React from "react"
import { ThemeProvider } from "#material-ui/styles"
import { CssBaseline } from "#material-ui/core"
import ThemeContext from "./themecontext"
import { defaultTheme, darkTheme } from "./themedefinition"
const ThemeWrapper = (children: React.ReactNode) => {
const [isDarkTheme, setDarkTheme] = React.useState(false);
const toggleDark = () => {
setDarkTheme(!isDarkTheme);
}
React.useEffect(() => {
if (window.matchMedia("(prefers-color-scheme: dark)").matches === true) {
setDarkTheme(true);
}
}, [])
return (
<ThemeContext.Provider value={{isDarkTheme, toggleDark}}>
<ThemeProvider theme={isDarkTheme ? darkTheme : defaultTheme}>
<CssBaseline />
{children}
</ThemeProvider>
</ThemeContext.Provider>
)
}
export default ThemeWrapper
Looks like a simple typo: you aren't destructuring children from your props, you're naming the first argument (the props) children.
- const ThemeWrapper = (children: React.ReactNode) => {
+ const ThemeWrapper = ({ children: React.ReactNode }) => {

useRef hook returns null in context

I am using useRef hook in my context. I am logging that out in useEffect hook inside context. I passing that useRef variable so that it can be accessed my components. Here is the context.
import { createContext, useContext, useRef, useEffect } from "react";
export const SearchContext = createContext({});
export const useSearch = () => useContext(SearchContext);
const SearchProvider = ({ children }) => {
const loader = useRef(null);
useEffect(() => {
console.log(loader); // returns {current: null}
}, []);
return (
<SearchContext.Provider
value={{
loader
}}
>
{children}
</SearchContext.Provider>
);
};
export default SearchProvider;
Next in my component I am adding ref property to div element with value loader which is coming from context. However, when I run this code I see {current: null} for loader. How can I use useRef in context to have access to DOM elements in component to make this work?
Here is my component
import { useSearch } from "./context";
const Component = () => {
const { loader } = useSearch();
return (
<div>
<div ref={loader}>Hello</div>
</div>
);
};
export default Component;
Here is the sandbox link.
https://codesandbox.io/s/nervous-jepsen-l83mf?file=/src/component.js:0-203

Getting SyntaxError when using lightweight-charts in NextJS

I'm trying to use the lightweight-charts package in my nextjs project, however when i try to call the createChart function I get this error in my nodejs console.
...\lightweight-charts\dist\lightweight-charts.esm.development.js:7
import { bindToDevicePixelRatio } from 'fancy-canvas/coordinate-space';
^^^^^^
SyntaxError: Cannot use import statement outside a module
Component:
import styled from "styled-components"
import { createChart } from 'lightweight-charts';
const Wrapper = styled.div``
const CoinPriceChart = () => {
const chart = createChart(document.body, { width: 400, height: 300 });
return <Wrapper></Wrapper>
}
export default CoinPriceChart
Page:
import styled from "styled-components"
import CoinPriceChart from "../../components/charts/CoinPriceChart"
const Wrapper = styled.div``
const CoinDetailPage = () => {
return (
<Wrapper>
<CoinPriceChart />
</Wrapper>
)
}
export default CoinDetailPage
Does someone have an idea what I could do to enable me to use the library within nextjs?
Thank you!
That because you are trying to import the library in SSR context.
Using next.js Dynamic with ssr : false should fix the issue :
import styled from "styled-components"
import dynamic from "next/dynamic";
const CoinPriceChart = dynamic(() => import("../../components/charts/CoinPriceChart"), {
ssr: false
});
const Wrapper = styled.div``
const CoinDetailPage = () => {
return (
<Wrapper>
<CoinPriceChart />
</Wrapper>
)
}
export default CoinDetailPage

Export a function as default while destructuring it for uses within the same class

I'm working with the React Context API, and I've got the following as an example
import React, { createContext, useState } from 'react';
const FooContext = createContext({
bar: false,
toggleBar: () => {}
});
export const FooContextConsumer = FooContext.Consumer;
export const FooContextProvider = ({ children }) => {
const [bar, setBar] = useState(false);
const toggleBar = () => setBar(!bar);
return (
<FooContext.Provider value={{ bar, toggleBar }}>
{children}
</FooContext.Provider>
)
};
export default FooContext;
Now, there's a lot of exports going on here. I know that the createContext functions has { Provider, Consumer } destructure properties available. Is there a way I can use that to compact this code? I've got something like this in mind but that isn't valid syntax unfortunately..
import React, { createContext, useState } from 'react';
export default ({ Provider, Consumer }) = createContext({
bar: false,
toggleBar: () => {}
});
export const FooContextConsumer = Consumer;
export const FooContextProvider = ({ children }) => {
const [bar, setBar] = useState(false);
const toggleBar = () => setBar(!bar);
return (
<Provider value={{ bar, toggleBar }}>
{children}
</Provider>
)
};
So, I want to export createContext function as the default, while using the Provider and Consumer properties of that function within the same file. Is this possible?
Of course I can do it with a const and an export default but I was wondering if this is possible as a one-liner.
You have to name your context in order to further use it in the file:
import React, { createContext, useState } from 'react';
const Context = createContext({
bar: false,
toggleBar: () => {}
});
export const FooContextConsumer = Context.Consumer;
export const FooContextProvider = ({ children }) => {
const [bar, setBar] = useState(false);
const toggleBar = () => setBar(!bar);
return (
<Context.Provider value={{ bar, toggleBar }}>
{children}
</Context.Provider>
)
};
export default Context;
The implementation I'd recommend for consuming the context:
import React, { createContext, useContext, useState } from 'react';
// ...
export const useFooContext = () => useContext(Context);
// ...
I would stick to your first example. It's clear which Context the Consumer, and Provider belong to.

"TypeError: dispatch is not a function" when using useReducer/useContext and React-Testing-Library

I'm having issues testing my components that use dispatch via useReducer with React-testing-library.
I created a less complex example to try to boil down what is going on and that is still having the same dispatch is not a function problem. When I run my tests, I am getting this error:
11 | data-testid="jared-test-button"
12 | onClick={() => {
> 13 | dispatch({ type: 'SWITCH' })
| ^
14 | }}
15 | >
16 | Click Me
Also, if I do a console.log(typeof dispatch) inside RandomButton, and I click on the button the output says function.
Here is the test in question.
import React from 'react'
import RandomButton from '../RandomButton'
import { render, fireEvent } from '#testing-library/react'
describe('Button Render', () => {
it('click button', () => {
const { getByTestId, queryByText } = render(<RandomButton />)
expect(getByTestId('jared-test-button')).toBeInTheDocument()
fireEvent.click(getByTestId('jared-test-button'))
expect(queryByText('My name is frog')).toBeInTheDocument()
})
})
Here is my relevant code:
RandomButton.js
import React, { useContext } from 'react'
import MyContext from 'contexts/MyContext'
const RandomButton = () => {
const { dispatch } = useContext(MyContext)
return (
<div>
<Button
data-testid="jared-test-button"
onClick={() => {
dispatch({ type: 'SWITCH' })
}}
>
Click Me
</Button>
</div>
)
}
export default RandomButton
MyApp.js
import React, { useReducer } from 'react'
import {myreducer} from './MyFunctions'
import MyContext from 'contexts/MyContext'
import RandomButton from './RandomButton'
const initialState = {
blue: false,
}
const [{ blue },dispatch] = useReducer(myreducer, initialState)
return (
<MyContext.Provider value={{ dispatch }}>
<div>
{blue && <div>My name is frog</div>}
<RandomButton />
</div>
</MyContext.Provider>
)
export default MyApp
MyFunctions.js
export const myreducer = (state, action) => {
switch (action.type) {
case 'SWITCH':
return { ...state, blue: !state.blue }
default:
return state
}
}
MyContext.js
import React from 'react'
const MyContext = React.createContext({})
export default MyContext
It is probably something stupid that I am missing, but after reading the docs and looking at other examples online I'm not seeing the solution.
I've not tested redux hooks with react-testing-library, but I do know you'll have to provide a wrapper to the render function that provides the Provider with dispatch function.
Here's an example I use to test components connected to a redux store:
testUtils.js
import React from 'react';
import { createStore } from 'redux';
import { render } from '#testing-library/react';
import { Provider } from 'react-redux';
import reducer from '../reducers';
// https://testing-library.com/docs/example-react-redux
export const renderWithRedux = (
ui,
{ initialState, store = createStore(reducer, initialState) } = {},
options,
) => ({
...render(<Provider store={store}>{ui}</Provider>, options),
store,
});
So, based upon what you've shared I think the wrapper you'd want would look something like this:
import React from 'react';
import MyContext from 'contexts/MyContext';
// export so you can test that it was called with specific arguments
export dispatchMock = jest.fn();
export ProviderWrapper = ({ children }) => (
// place your mock dispatch function in the provider
<MyContext.Provider value={{ dispatch: dispatchMock }}>
{children}
</MyContext.Provider>
);
and in your test:
import React from 'react';
import RandomButton from '../RandomButton';
import { render, fireEvent } from '#testing-library/react';
import { ProviderWrapper, dispatchMock } from './testUtils';
describe('Button Render', () => {
it('click button', () => {
const { getByTestId, queryByText } = render(
<RandomButton />,
{ wrapper: ProviderWrapper }, // Specify your wrapper here
);
expect(getByTestId('jared-test-button')).toBeInTheDocument();
fireEvent.click(getByTestId('jared-test-button'));
// expect(queryByText('My name is frog')).toBeInTheDocument(); // won't work since this text is part of the parent component
// If you wanted to test that the dispatch was called correctly
expect(dispatchMock).toHaveBeenCalledWith({ type: 'SWITCH' });
})
})
Like I said, I've not had to specifically test redux hooks but I believe this should get you to a good place.

Categories