sql.js react context provider - reactjs

I want to run a local-first app, so node.js is not available.
I try to provide a sqljs database via context provider.
I mostly times get the error
TypeError: Cannot read properties of null (reading 'exec')
The code looks as following
SQLiteContext.js
import React, { createContext, useEffect, useState } from "react";
export const SQLiteContext = createContext();
const SQLiteProvider = ({ children }) => {
const [SQL, setSQL] = useState(null)
const [sqliteDB, setSqliteDB] = useState(null)
useEffect(() => {
initSqlJs({ locateFile: file => `/js/${file}` }).
then((aSQL)=>{
setSQL(aSQL)
setSqliteDB(new aSQL.Database())
});
}, []);
return (
<SQLiteContext.Provider value={{ SQL,setSQL ,sqliteDB,setSqliteDB }}>
{children}
</SQLiteContext.Provider>
)
}
export { SQLiteProvider };
_document.js - static html
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document() {
return (
<Html>
<Head />
<body>
<Script src='/js/sql-wasm.js' strategy="beforeInteractive" />
<Main />
<NextScript />
</body>
</Html>
)
}
App.js
import React, { useContext, useEffect } from "react";
import { SQLiteContext } from "../contexts/SQLiteContext";
export default function App() {
const {SQL,sqliteDB} = useContext(SQLiteContext)
const res = sqliteDB.exec("SELECT * FROM hello")
return (
<>
test
{res}
</>
)
}
index.js
import { SQLiteProvider } from "../contexts/SQLiteContext"
import App from "../components/App"
export default function Home() {
return (
<div>
<SQLiteProvider>
<App />
</SQLiteProvider>
</div>
)
}
The sqljs/initSqlJs works fine.
The Problem seems to be, that if It gets executed, it's not available yet
I tried alrady
put all code in one file
put the query inside the InitSqlJs(...).then((...)=>{})
do the initSqlJs in the static.html with window.db
It seems I need to get the DB Handle before the ContextProvider, but where?

Related

How to rewrite google Tag Manager Args from typescript to react

I am trying to insert google tags into my react site so I copied a code from a typescript code I have but I get the error Type annotations can only be used in TypeScript files. so now I have no idea on how to rewrite this code in an acceptable format in reactjs. Please help using the code below
_app.js
import React, {useEffect} from 'react'
import TagManager, {TagManagerArgs} from 'react-gtm-module'
import { Toaster } from 'react-hot-toast'
import './Product.scss'
import {Layout} from '../components'
import '../styles/globals.css'
import { StateContext } from '../context/StateContext'
function MyApp({ Component, pageProps }) {
const gtmId = process.env.NEXT_PUBLIC_GTM_ID || "";
const tagManagerArgs: TagManagerArgs = {
gtmId,
}
useEffect(() => {
TagManager.initialize(tagManagerArgs)
}, [])
return (
<StateContext>
<Layout>
<Toaster />
<Component {...pageProps} />
</Layout>
</StateContext>
)
}
export default MyApp
You can remove the type annotation and update like the following code.
const tagManagerArgs = {
gtmId,
}
You can go through this link.

How to resolve No `Web3Provider Found` Exception web3-react?

I am getting a No <Web3Provider ...> Found. when my React Typescript app loads and tries to call the context manager method setFirstValidConnector as described in the latest 5.0.3 web3-react here
This is the full code:
./wallet/connect.ts
import { Connectors } from 'web3-react'
const { InjectedConnector } = Connectors
const metamask = new InjectedConnector({ supportedNetworks: [1, 4] })
const alchemy = new Connectors.NetworkOnlyConnector({ providerURL: "https://eth-rinkeby.alchemyapi.io/v2/..."});
export const connectors = { metamask, alchemy }
App.tsx
import React, { useEffect } from 'react';
import Web3Provider, { useWeb3Context } from 'web3-react';
import { connectors } from './wallet/connect';
export default function App() {
const context = useWeb3Context()
useEffect(() => {
context.setFirstValidConnector(['metamask', 'alchemy']) // => No <Web3Provider ...> Found.
}, [])
return (
<Web3Provider
connectors={connectors}
libraryName={'ethers.js'}
>
<div className="App">
<ButtonAppBar />
<AboutSection />
<Workspace />
</div>
</Web3Provider>
);
};
Does anyone happen to see where I'm going wrong? Thank you!

NextJS - Add provider for only a few specific pages

Is it possible for me to add a provider only for a specific route instead of going to my entire app in the next js?
my "pages" folder
My context file [server.jsx]
import { createContext, useState } from 'react';
const ServerContext = createContext({});
export const ServerProvider = ({ children }) => {
const [ data, setData] = useState(null);
return (
<ServerContext.Provider value={{ data, setData }}>
{children}
</ServerContext.Provider>
);
};
export default ServerContext;
And instead of passing it on to the entire app as below, I wanted to just pass it to my dashboard routes
[_app.jsx]
import { Provider } from 'next-auth/client';
import { ThemeProvider } from 'styled-components';
import GlobalStyles from '#/styles/global';
import theme from '#/styles/theme';
import { ServerProvider } from '#/contexts/server';
function MyApp({ Component, pageProps })
{
return (
<ThemeProvider theme={theme}>
<Provider session={pageProps.session} >
<ServerProvider>
<Component {...pageProps} />
<GlobalStyles />
</ServerProvider>
</Provider>
</ThemeProvider>
);
}
export default MyApp;
You can use the files in pages folder to wrap your route.
This file would be something like pages/server/index.js, if is a static one.
Example:
import { ServerProvider } from "..."
import { ServerPageOrSomething } from "..."
export default function MyRoute({ ...props }) {
return (
<ServerProvider>
// my components, like:
<ServerPageOrSomething {...props} />
</ServerProvider>
)
}
//your Next stuff here
export async function getServerSideProps(props) {
// ...
return {
props: {
//...
},
};
}
The Next stuff and the ...props depends in what you're using Next for. You can just ignore then if you don't need to process anything (get data, etc)

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.

Resources