I created the following context.tsx:
import { createContext } from 'react';
export interface LibsAndComponentsInterface {
data: unknown[];
}
export const LibsAndComponentsContext = createContext<
LibsAndComponentsInterface | undefined
>(undefined);
And use it in my Wrapper component:
import { ReactNode, useContext, useEffect, useMemo, useState } from 'react';
import { LibsAndComponentsContext } from 'core/context/dataContext';
import { useApiService } from 'common/hooks/useApiService';
import { useAuth } from 'common/contexts/Authentication';
// Wrap your App component with this
export function LibsAndComponentsProvider({
children,
}: {
children: ReactNode;
}) {
const [libs, setLibs] = useState<unknown[]>([]);
const [components, setComponents] = useState<unknown[]>([]);
const { isAuthenticated } = useAuth();
const { sendRequest } = useApiService();
useEffect(() => {
.....
}, []);
const ctxValue = useMemo(
() => ({
data: [...libs, ...components],
}),
[libs, components],
);
return (
<LibsAndComponentsContext.Provider value={ctxValue}>
{children}
</LibsAndComponentsContext.Provider>
);
}
export function useLibsAndComponents() {
const ctx = useContext(LibsAndComponentsContext);
if (ctx == null) {
throw new Error(
'useLibsAndComponents must be inside LibsAndComponentsProvider',
);
}
return ctx;
}
I get now an error returned for <LibsAndComponentsContext.Provider value={ctxValue}>
'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.
'React' must be in scope when using JSX
I do not see where I am referencing as a module, could somebody point me to the error?
Adding import React from "react"; was the embarrassing solution.
Related
I have Const like this in my config.service.ts file
export const mysettings={
userid:"12324",
conf:{
sessionDuration:30,
mac:"LON124"
}
}
I am using this constant in some components
But instead of hardcoding those values in const I need to get that at runtime from JSON file in my public folder
So I have a function like this as well
async getConfig(){
const data=await fetch("./data/data.json")
.then((response) => response.json())
.then((json) => return json );
}
So in my data.json files I have those values for the const and I need these values in that JSON file to updated or the JSON file itself sometimes replaced.
Please help me how to accomplish that?
You can create a Context and ContextProvider to load your data from json file, set it to state variable and just pass it to Context consumers:
DataContext.tsx:
import { createContext, useState, useContext, useEffect, PropsWithChildren } from "react";
import asyncData from "./asyncData";
interface IData {
userid: string;
conf: {
sessionDuration: number;
mac: string;
};
}
interface IContextValue {
data: IData;
}
const StateContext = createContext<IContextValue>(null!);
export function DataContextProvider(props: PropsWithChildren) {
const [data, setData] = useState<IData>(undefined!);
useEffect(() => {
asyncData.then((json) => setData(json)).catch(console.error);
}, []);
// Optionally - wrap with useMemo
const contextValue: IContextValue = {
data: data
};
return (
<StateContext.Provider value={contextValue}>
{data && props.children}
</StateContext.Provider>
);
}
export default function useDataContext() {
const context = useContext(StateContext);
if (!context) {
throw new Error(
"useDataContext must be used within the DataContextProvider"
);
}
return context;
}
Updates to index.tsx:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { DataContextProvider } from "./DataContext";
import App from "./App";
import asyncData from "./asyncData";
asyncData.then((config) => {
console.log("Config is fetched before render: ", config);
const rootElement = document.getElementById("root");
const root = createRoot(rootElement!);
root.render(
<StrictMode>
<DataContextProvider>
<App />
</DataContextProvider>
</StrictMode>
);
});
Usage:
import useDataContext from "./DataContext";
export default function App() {
const { data } = useDataContext();
return <div className="App">{JSON.stringify(data)}</div>;
}
asyncData.ts:
const asyncData = fetch("./data/data.json").then((response) => response.json());
export default asyncData;
I have a question about userContext in react with typescript.
First I define it in RubroContext.tsx
import { createContext, useContext } from "react";
import { RubroType1, RubroType2 } from "../Interfaces/interfaces";
export const RubroContext1 = createContext <Partial<RubroType1>>({})
export const RubroContext2 = createContext <Partial<RubroType2>>({})
export const useRubroContext1 = () => useContext(RubroContext1);
export const useRubroContext2 = () => useContext(RubroContext2);
this is interfaces.tsx
export type RubroType1 = {
rubrosItem1 : itemRubro;
setItemRubro1: Dispatch<SetStateAction<itemRubro >>;
}
export type RubroType2 = {
rubrosItem2 : itemRubro;
setItemRubro2 : Dispatch<SetStateAction<itemRubro >>;
}
and this is how I implement it in the components
const CompletarRubros = (props:{setIsReg:any,email:any, clientType:any}) => {
const {rubrosItem1,setItemRubro1} = useRubroContext1 ()
const {rubrosItem2,setItemRubro2} = useRubroContext2 ()
const rubro = useRef ("first")
const radius = useRef (1)
const description = useRef ("test")
useEffect(() => {
setItemRubro1!({
rubro:rubro.current,
radius:String(radius),
description:descripcion.current,
calificacion:0,
})
}, []);
//...........
}
The problem is that the code is not updated. When I want to access rubrosItem1 in other components, the information that should have been saved in the useEffect is not there. I am doing something wrong?
for example in another component
const Test= () => {
const {rubrosItem1,setItemRubro1} = useRubroContext1 ()
useEffect(() => {
console.log(rubrosItem1.rubro)
// it does not show anything
}, []);
}
You should create a RubroContext1Provider component and declare the context value as its local state. So that the children of the RubroContext1Provider component and share the context value(stored in its state).
E.g.
RubroContext.tsx:
import {
createContext,
Dispatch,
SetStateAction,
useContext,
useState,
} from 'react';
import * as React from 'react';
type itemRubro = any;
export type RubroType1 = {
rubrosItem1: itemRubro;
setItemRubro1: Dispatch<SetStateAction<itemRubro>>;
};
export type RubroType2 = {
rubrosItem2: itemRubro;
setItemRubro2: Dispatch<SetStateAction<itemRubro>>;
};
export const RubroContext1 = createContext<Partial<RubroType1>>({});
export const RubroContext2 = createContext<Partial<RubroType2>>({});
export const useRubroContext1 = () => useContext(RubroContext1);
export const useRubroContext2 = () => useContext(RubroContext2);
export const RubroContext1Provider = ({ children }) => {
const [value, setValue] = useState();
return (
<RubroContext1.Provider
value={{
rubrosItem1: value,
setItemRubro1: setValue,
}}
>
{children}
</RubroContext1.Provider>
);
};
CompletarRubros.tsx:
import { useEffect, useRef } from 'react';
import { useRubroContext1 } from './RubroContext';
export const CompletarRubros = () => {
const { rubrosItem1, setItemRubro1 } = useRubroContext1();
const rubro = useRef('first');
const radius = useRef(1);
const description = useRef('test');
useEffect(() => {
setItemRubro1({
rubro: rubro.current,
radius: String(radius),
description: description.current,
calificacion: 0,
});
}, []);
return null;
};
Test.tsx:
import { useRubroContext1 } from './RubroContext';
export const Test = () => {
const { rubrosItem1 } = useRubroContext1();
console.log('[Test]:', rubrosItem1?.rubro);
return null;
};
App.tsx:
import * as React from 'react';
import { CompletarRubros } from './CompletarRubros';
import { RubroContext1Provider } from './RubroContext';
import './style.css';
import { Test } from './Test';
export default function App() {
return (
<div>
<RubroContext1Provider>
<CompletarRubros />
<Test />
</RubroContext1Provider>
</div>
);
}
The console logs:
[Test]:undefined
[Test]:undefined
[Test]:first
[Test]:first
stackblitz
I want to know if is possible to navigate between screens, using like a context api, or something else, where I can get the "navigateTo" function in any component without passing by props. And of course, without the cycle dependency problem.
Example with the cycle dependency problem
NavigateContext.tsx:
import React, { createContext, useMemo, useReducer } from 'react'
import { Home } from './pages/Home'
interface NavigateProps {
navigateTo: (screenName: string) => void
}
export const navigateContext = createContext({} as NavigateProps)
const reducer = (state: () => JSX.Element, action: { type: string }) => {
switch (action.type) {
case 'home':
return Home
default:
throw new Error('Page not found')
}
}
export function NavigateContextProvider() {
const [Screen, dispatch] = useReducer(reducer, Home)
const value = useMemo(() => {
return {
navigateTo: (screenName: string) => {
dispatch({ type: screenName })
},
}
}, [])
return (
<navigateContext.Provider value={value}>
<Screen />
</navigateContext.Provider>
)
}
Home.tsx:
import React, { useContext, useEffect } from 'react'
import { Flex, Text } from '#chakra-ui/react'
import { navigateContext } from '../NavigateContext'
export function Home() {
const { navigateTo } = useContext(navigateContext)
useEffect(() => {
setTimeout(() => {
navigateTo('home')
}, 2000)
}, [])
return (
<Flex>
<Text>Home</Text>
</Flex>
)
}
Yes, this is possible, but you'll need to maintain the list of string view names independently from your mapping of them to their associated components in order to avoid circular dependencies (what you call "the cycle dependency problem" in your question):
Note, I created this in the TS Playground (which doesn't support modules AFAIK), so I annotated module names in comments. You can separate them into individual files to test/experiment.
TS Playground
import {
default as React,
createContext,
useContext,
useEffect,
useState,
type Dispatch,
type ReactElement,
type SetStateAction,
} from 'react';
////////// views.ts
// Every time you add/remove a view in your app, you'll need to update this array:
export const views = ['home', 'about'] as const;
export type View = typeof views[number];
export type ViewContext = {
setView: Dispatch<SetStateAction<View>>;
};
export const viewContext = createContext({} as ViewContext);
////////// Home.ts
// import { viewContext } from './views';
export function Home (): ReactElement {
const {setView} = useContext(viewContext);
useEffect(() => void setTimeout(() => setView('home'), 2000), []);
return (<div>Home</div>);
}
////////// About.ts
// import { viewContext } from './views';
export function About (): ReactElement {
const {setView} = useContext(viewContext);
return (
<div>
<div>About</div>
<button onClick={() => setView('home')}>Go Home</button>
</div>
);
}
////////// ContextProvider.tsx
// import {viewContext, type View} from './views';
// import {Home} from './Home';
// import {About} from './About';
// import {Etc} from './Etc';
// Every time you add/remove a view in your app, you'll need to update this object:
const viewMap: Record<View, () => ReactElement> = {
home: Home,
about: About,
// etc: Etc,
};
function ViewProvider () {
const [view, setView] = useState<View>('home');
const CurrentView = viewMap[view];
return (
<viewContext.Provider value={{setView}}>
<CurrentView />
</viewContext.Provider>
);
}
I have this problem, can anyone help me?
TypeError: customers.map is not a function.
I've always used it that way and I've never had any problems.
Its about data integration.
Basically is that, please anyone can help me?
import React, { useState, useEffect } from "react";
import { List, Card } from "antd";
import { data } from "../../../mocks/customers";
import { DeleteCustomerButton } from "#components/atoms/DeleteCustomerButton";
import { CustomersEditButton } from "#components/atoms/CustomersEditButton";
import { useContext } from "../../../contexts/context";
const { Meta } = Card;
const CustomersCardList: React.FC = () => {
const customers: any = useContext();
return (
<div>
{customers.map((customer, key) => { })}</div>)
}
//context.tsx
import * as React from 'react';
import axios from 'axios';
export const AccountContext = React.createContext({});
export const useContext = () => React.useContext(AccountContext);
interface AccounterContextProviderProps {
value: any
};
export const AccounterContextProvider: React.FC<AccounterContextProviderProps> = ({ children, value }) => {
const [customers, setCustomers] = React.useState<any>([]);
React.useEffect(() => {
const getCustomers = async () => {
const result = await axios.get("http://localhost:3333/customers");
setCustomers(result.data);
}
getCustomers();
}, []);
console.log(customers);
return (
<AccountContext.Provider value={{ ...value, customers }}>
{children}
</AccountContext.Provider>
)
};
Any can be anything not only array, so it will not have a map method. Use const customers:any[] = useContext() instead
When using useContext() I'm getting an empty object.
I created a file that contains my Context, Provider, and Consumer
src/utils/notesContext.js
import PropTypes from "prop-types"
import React, { createContext, useState } from "react"
export const Context = createContext({})
export const Provider = props => {
const {
notes: initialNotes,
selectedNote: initialSelectedNotes,
children,
} = props
const [notes, setNotes] = useState([[""]])
const [selectedNote, setSelectedNote] = useState([[""]])
const addNewNote = note => {
console.log(note)
}
const notesContext = {
notes,
setNotes,
selectedNote,
setSelectedNote,
addNewNote,
}
return <Context.Provider value={notesContext}>{children}</Context.Provider>
}
export const { Consumer } = Context
Provider.propTypes = {
notes: PropTypes.array,
selectedNote: PropTypes.object,
}
Provider.defaultProps = {
notes: [],
selectedNote: {},
}
Then within my index file, I have the following
src/pages/index.js
import React, { useContext } from "react"
import { Context } from "../utils/notesContext"
const Index = ({ data, location }) => {
const initNote = data.note
const notesContext = useContext(Context) // notesContext is empty
const { notes, selectedNote, setSelectedNote, addNewNote } = notesContext
addNewNote(initNote)
...
}
Did I set up something incorrectly with my context or provider? Another thing worth mentioning is that this is in a Gatsby application so not sure if this could be causing an underlying issue that I'm not aware of.
Thanks in advance!
Your Index component should be used inside notesContext Provider.
Read more about Context.Provider.
import { Context, Provider } from "./notesContext";
const Index = () => {
const notesContext = useContext(Context);
console.log(notesContext); // now this is not an empty object
return <p>Index</p>;
};
export default function App() {
// notice here, we're wrapping Index inside our Provider
return (
<Provider>
<Index />
</Provider>
);
}