React: TypeError: _useContext is undefined - javascript

I'm trying my hand at TypeScript and React. I have a functional component (code below) that is supposed to consume a context with useContext, but it is showing me this weird error that I cannot find a solution to.
If I do not use TS, and go with JSX, it works just fine.
Edit: Screenshot>
Code:
AppProvider.tsx
import React, { useState, useEffect } from "react";
// Application's context (for general application-wide usage)
const AppContext: any = React.createContext(null);
// this will be used below in the componet we will export
export const AppContextProvider = AppContext.Provider;
export const AppProvider: React.FC = (props: any) => {
const [appName, setAppName] = useState("Blood Donation");
const [appUser, setAppUser]: any = useState(null);
const [appInfoBusy, setAppInfoBusy] = useState(false); // working to get or set data
useEffect(() => {
getAppInfo();
}, []);
const getAppInfo = () => {
setTimeout(() => {
setAppName("Test");
setAppUser({
name: "Admin",
email: "test#test.com",
role_id: 100
});
}, 3000);
};
return (
<AppContextProvider
value={{
appName: appName,
appInfoBusy: appInfoBusy,
appUser: appUser
}}
>
{props.children}
</AppContextProvider>
);
};
Consumer: Login.tsx
import React, { useState, useEffect, useContext } from "react";
import {
Button,
Card,
Elevation,
FormGroup,
InputGroup,
Drawer,
Classes,
H4,
Callout,
H5
} from "#blueprintjs/core";
//#ts-ignore
import ReCAPTCHA from "react-google-recaptcha";
import logo from "../../assets/images/logo.png";
import "../../scss/Login.scss";
import { RecaptchaKey } from "../../shared/Info";
import { AppContextProvider } from "../../shared/context/AppProvider";
const Login: React.FC = props => {
const [email, setEmail]: React.ComponentState = useState();
const [password, setPassword]: any = useState();
const [isOpen, setIsOpen]: any = useState();
const [resetEmail, setResetEmail]: any = useState();
const [emailSent, setEmailSent]: any = useState();
const [captchaOk, setCaptchaOk]: any = useState(false);
const [working, setWorking]: any = useState(false);
// context
const { appName, appUser, appInfoBusy } = useContext(AppContextProvider);
/**
* Handles lifecycle hooks
*/
useEffect(() => {
// when component is mounted
}, []);
/**
* Handles Captcha change
* #param value
*/
const recaptchaChange = (value: any) => {
setCaptchaOk(value ? true : false);
};
const handleRecoverySubmit = () => {
setWorking(true);
setTimeout(() => {
setEmailSent(true);
setWorking(false);
}, 3000);
};
return (
<div id="loginPage">
... removed for brevity ...
</div>
);
};
export default Login;
Any help is gratefully thanked. React and dependencies are all latest as of date.

I was using the context provider instead of the context itself inside useContext(), I should have used useContext(AppContext) instead.
Commentary removed because stackoverflow.

The error is _useContext not defined. The issue is different than what it is actually referring to.
you created a context called as AppContext
and then you export this as
export const AppContextProvider = AppContext.Provider;
You have done correct till this stage.
The problem lies at consumer part i.e. login.tsx file.
you are importing a name file inside a curly braces which is not correct, because the context is exported as a name variable. You simply need to write
import AppContextProvider from "../../shared/context/AppProvider";
That's it, and when you are calling this context using useContext hooks, then the actual state that you are looking for get accessed and no issue will further persist.
Note: Don't use {} for importing named exports.
reference: When should I use curly braces for ES6 import?

Related

Connecting two contexts return undefined values in the consumer context react hooks

I really do not understand why this is not working, basically, I have a header component with its own context. On the other hand, I have a popOver component that goes inside the header, and this popOver also has its own context.
Now, there is a list of elements that are rendered inside the popOver, the user picks which elements to render, and such list needs to be rendered simultaneously in the header, for that reason I am trying to keep both contexts synchronized, the problem appears when I try to consume the header context inside the popOver context, the values consumed appear to be undefined.
const HeaderContext = createContext();
export const HeaderProvider = ({ children }) => {
const [headChipList, setHeadChipList] = useState([]);
const [isChipOpen, setIsChipOpen] = useState(false);
useEffect(() => {
if (headChipList.length) setIsChipOpen(true);
}, [headChipList]);
return (
<HeaderContext.Provider value={{ headChipList, setHeadChipList, isChipOpen, setIsChipOpen }}>
{children}
</HeaderContext.Provider>
);
};
export const useHeaderContext = () => {
const context = useContext(HeaderContext);
if (!context) throw new Error('useHeaderContext must be used within a HeaderProvider');
return context;
};
As you can see at the end there's a custom hook that allows an easier consumption of the context and also is a safeguard in case the custom hook is called outside context, the popOver context follows this same pattern:
import React, { useState, useContext, createContext, useEffect } from 'react';
import { useHeaderContext } from '(...)/HeaderProvider';
const PopoverContext = createContext();
export const PopoverProvider = ({ children }) => {
const { setHeadChipList, headChipList } = useHeaderContext; // this guys are undefined
const [menuValue, setMenuValue] = useState('Locations with Work Phases');
const [parentId, setParentId] = useState('');
const [chipList, setChipList] = useState([]);
const [locations, setLocations] = useState([]);
useEffect(() => setChipList([...headChipList]), [headChipList]);
useEffect(() => setHeadChipList([...chipList]), [chipList, setHeadChipList]);
return (
<PopoverContext.Provider
value={{
menuValue,
setMenuValue,
chipList,
setChipList,
parentId,
setParentId,
locations,
setLocations
}}
>
{children}
</PopoverContext.Provider>
);
};
export const usePopover = () => {
const context = useContext(PopoverContext);
if (!context) throw new Error('usePopover must be used within a PopoverProvider');
return context;
};
I would really appreciate any highlight about this error, hopefully, I will be able to learn how to avoid this type of error in the future
You're not calling the useHeaderContext function. In PopoverProvider, change the line to
const { setHeadChipList, headChipList } = useHeaderContext();

react useContext setState is not a function

I'm working on a react app and I need to keep an array of names in my global state like on this file:
import React from "react";
import { useState } from "react";
const initialState = {
nameList: [],
test: 'hello'
}
export const Context = React.createContext()
const Data = ({ children }) => {
const [state, setState] = useState(initialState)
return(
<Context.Provider value={[state, setState]}>
{children}
</Context.Provider>
)
}
export default Data
However, when I try to set "nameList" to a new value in this other file:
const [state, setState] = useContext(Context);
const [currName, setCurrName] = useState('');
const handleAddName = () => {
setState.nameList(prevState => [...prevState, currName])
}
I get a "setState.nameList is not a funtion" error and I can't find nor understand the reason why, any help would be much appreciated
You're updating the state incorrectly, here's how to do it in your case:
const handleAddName = () => {
setState(prevState => ({
...prevState,
nameList: [...prevState.nameList, currName]
}))
}
This will make sure that the state is updated immutably.
setState.nameList is wrong because setState is a function but not an object that will magically have the keys of your state.

Too many re-renders. React limits the number of renders to prevent an infinite loop when passing states?

I am trying to access state from one component to another
FetchCompo.js
// import React from "react";
import React, { useState, useEffect } from "react";
//more imports
const FetchUserItems= () => {
//some state
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
let userName = //somecode
setUserFirstName(userName);
let userItemsData= userData.MyArray.items;
if (userItemsData.length === 0) {
const emptyItems = [
{
//obj data
},
];
setUserItem(emptyItems );
} else {
//someData
setUserItem(userItemsData);
}
return { userFirstName, userItem};
};
export default FetchCompo;
I wanted to use userFirstName, userItem in the another Test.js component.
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}
when I am trying to get the userFirstName, userItem in the Test.js component then getting error of Too many renders
looking for a solution how i can access these state userFirstName, userItem form one component to another.
You're actually importing the React Component not the FetchUserItems helper function...
import FetchCompofrom "../myFunctions/FetchCompo";
But you could do something like...
const [userFirstName, setUserFirstName] = useState('');
const [userItem, setUserItem] = useState([]);
const FetchUserItems = () => {
/**
* Make it plain helper function for fetching userItems
* Do-not set-state here...
*/
return { userFirstName, userItem };
};
export const FetchUserItems;
/** In your component ... say in useEffect */
const result = FetchUserItems();
/** setState here in case of result */
In Test.js
import { FetchUserItems } "../myFunctions/FetchCompo";
Your are using setUserFirstName(userName) in FechUserItems, outside useEffect or normal function, this will provoque the component to re-render indefinitely because setting the states provoques re-rendering.
I would suggest to make FetchUserItems a normal function, because you are not rendering anything in it. You could use only Test comp for it.
The Test comp would be something like this:
// import React from "react";
import React, { useState, useEffect } from "react";
import FetchCompofrom "../myFunctions/FetchCompo";
//more imports
const Test = () => {
const [userFirstName, setUserFirstName] = useState("");
const [userItem, setUserItem] = useState([]);
useEffect(() => fetchUserFirstName, [])
const fetchUserFirstName = () => {
// your code here and
// setUserFirstName in the end
}
//Wanted to use userFirstName in Test.js component
const { userFirstName, userItem } = FetchCompofrom();
return (
<div>{userFirstName}</div>
)
}

calling method on sub component in React functional component via ref

I'm using syncfusion react controls to add some functionality to my app. I want to call a method on the control in my functional component, but I haven't been able to get the ref set properly:
import React, {createRef, useEffect, useState} from "react";
import {AutoCompleteComponent} from "#syncfusion/ej2-react-dropdowns";
import "#syncfusion/ej2-base/styles/bootstrap.css";
import "#syncfusion/ej2-react-inputs/styles/bootstrap.css";
import "#syncfusion/ej2-react-dropdowns/styles/bootstrap.css";
const UserLookup = ({userSelected}) => {
const [searchString, setSearchString] = useState('');
const [items, setItems] = useState([]);
const helper = new QueryHelper();
let listObj = createRef();
const searchStringChanged = (args) => {
console.log(args.text);
if (args.text.length > 3) {
setSearchString(args.text);
}
}
const optionSelected = (event) => {
memberSelected(event.item.id);
}
useEffect(() => {
fetch('http://example.com/myendpoint')
.then(response.map((result) => {
listObj.current.showPopup(); // <-- this method should be called on the autocomplete component
return {
id: result.contactId,
label: result.firstName + ' ' + result.lastName
}
}))
.then(data => console.log(data));
}, [searchString]);
return (
<AutoCompleteComponent
id="user_search"
autofill={true}
dataSource={items}
fields={
{
value: 'label'
}
}
filtering={searchStringChanged}
select={optionSelected}
popupHeight="250px"
popupWidth="300px"
placeholder="Find a contact (optional)"
ref={listObj}
/>
);
};
export default UserLookup;
this always throws an error that Cannot read property 'showPopup' of null -- how do you set the ref for the instance of the AutoCompleteComponent so that you can call methods on it?
We can get the reference for the AutoComplete when it's rendered as a functional component with help of using useRef method instead of createRef method. Please find the modified sample from below.
Sample Link: https://codesandbox.io/s/throbbing-shadow-ddsmf

What is the performance impact of useContext vs props? [duplicate]

I used a structure using React Hooks. It is based on a global Context that contains a combination of reducers (as in the Redux).
Also, I widely use custom hooks to separate logic.
I have a hook that contains asynchronous API requests and it has become quite cumbersome and I have the opportunity to split almost every function of this hook into other hooks, but each of these functions uses a global context (more precisely - dispatch from useReducer()).
So, questions:
Is it ok to use useContext() in every hook who needs it?
How will it affect performance if, for example, I create 10 custom hooks that use useContext() internally and use them in the component.
Example:
providers/Store.js
import React, { createContext, useReducer } from 'react';
export const StoreContext = createContext();
export const StoreProvider = ({ children }) => {
/**
* Store that contains combined reducers.
*/
const store = useReducer(rootReducer, initialState);
return (
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
};
hooks/useStore.js
import { useContext } from 'react';
import { StoreContext } from '../providers';
export const useStore = () => useContext(StoreContext);
hooks/useFoo.js
import { useCallback } from 'react';
import { useStore } from './useStore';
export const useFoo = () => {
const [, dispatch] = useStore();
const doFoo = useCallback(
async params => {
dispatch(actions.request());
try {
const res = await SomeService.getSomething(params);
dispatch(actions.add(res));
dispatch(actions.success());
} catch (error) {
dispatch(actions.failure());
}
},
[dispatch]
);
return { doFoo };
};
hooks/useBar.js
import { useCallback } from 'react';
import { useStore } from './useStore';
export const useBar = () => {
const [, dispatch] = useStore();
const doBar = useCallback(
async params => {
dispatch(actions.request());
try {
const res = await SomeService.getSomething(params);
dispatch(actions.success());
dispatch(actions.add(res));
} catch (error) {
dispatch(actions.failure());
}
},
[dispatch]
);
return { doBar };
};
hooks/useNext.js
...
import { useStore } from './useStore';
export const useNext = () => {
const [, dispatch] = useStore();
...
};
components/SomeComponent.js
const SomeComponent = () => {
// use context
const [store, dispatch] = useStore();
// and here is the context
const { doFoo } = useFoo();
// and here
const { doBar } = useBar();
// and here
useNext();
return (
<>
<Button onClick={doFoo}>Foo</Button>
<Button onClick={doBar}>Bar</Button>
// the flag is also available in another component
{store.isLoading && <Spin />}
</>
)
}
Internally, hooks can reference a state queue owned by component. (Under the hood of React’s hooks system - Eytan Manor
)
useContext is just to reference a global state from the relative Context Provider. There is almost no overhead from useContext as you are concerned.

Categories