React.useContext appearing as undefined - reactjs

This is my first time using the React context hooks in an app and my context default value keeps appearing as "undefined."
Troubleshooting so far:
I've made sure React.createContext is in a separate file (context.js)
I've made sure the child component is wrapped within the Provider
I'm providing a default value to React.createContext()
All of my code can be found in this CodeSandbox link below:
https://codesandbox.io/s/react-context-troubleshooting-ojnb2?file=/src/child.js

In your App.js file, you're passing value a string:
import React, { useContext } from "react";
import { SelectedBackgroundContext } from "./context";
import Child from "./child";
function App() {
const { selectedBackground } = useContext(SelectedBackgroundContext);
// selectedBackground is just a string, so value = "https://...", which is invalid, the value, in your case, should be an object
return (
<SelectedBackgroundContext.Provider value={selectedBackground}>
<Child />
</SelectedBackgroundContext.Provider>
);
}
export default App;
Instead, value needs to be an object, with a selectedBackground property that contains the string:
import React, { useContext } from "react";
import { SelectedBackgroundContext } from "./context";
import Child from "./child";
function App() {
const { selectedBackground, selectBackground } = useContext(
SelectedBackgroundContext
);
// alternatively, just collect all context, without destructuring,
// and pass it to the "value" prop: value={context}
// const context = useContext(SelectedBackgroundContext);
// you're also missing the "selectBackground" function, which should be added to this "value" prop
return (
<SelectedBackgroundContext.Provider
value={{ selectedBackground, selectBackground }}
>
<Child />
</SelectedBackgroundContext.Provider>
);
}
export default App;
Since you've created context using an object:
{
selectedBackground:
"https://lp-cms-production.imgix.net/2019-06/81377873%20.jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4",
selectBackground: () => {}
}
The value property of the provider should also be an object!
value={{ selectedBackground, selectBackground }}
Working demo:

I have changed your code to following and its working.
import React, { useContext } from "react";
import { SelectedBackgroundContext } from "./context";
export default function Child() {
const selectedBackground = useContext(SelectedBackgroundContext);
// you can comment out line5 above and uncomment line7 below to verify all other code works
//let selectedBackground = 'https://lp-cms-production.imgix.net/2019-06/81377873%20.jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4'
const renderSelected = (context) => {
console.log("Background img src is: " + context); //appears as undefined
if (context) {
return (
<img
style={{ height: "200px" }}
src={context}
key={context + "Thumbnail"}
alt={"thumbnail of " + context}
/>
);
} else {
return <p>None</p>;
}
};
return (
<div>
<p>Background:}</p> {renderSelected(selectedBackground)}
</div>
);
}
because your are not passing object from context value that is why no need of
const {selectedBackground} = useContext(SelectedBackgroundContext);
more about deconstructing variable
https://www.javascripttutorial.net/es6/javascript-object-destructuring/

Related

Why the context provider doesn't pass props with useContext inside children as function

I'm trying to access the context props using useContext inside a children as function but the props are always the default
import React, { createContext, useContext } from "react";
const Context = createContext(undefined);
export default function Component() {
return (
<Context.Provider value={{ variable: 123 }}>
{(() => {
const context_value = useContext(Context);
console.log(context_value);
return <></>;
})()}
</Context.Provider>
);
}
Output:
undefined
I know I can use the Context.Consumer but in my real case is different because I'm creating a hook in another file to call useContext(context) instead of exporting Context
You can only use hooks (like useContext) at the top-level of function components (or in custom hooks), but not in the place where you're using it. Try to put a child component inside the provider and to put the useContext inside that child component.
this works for me, hope this will helpful to you also.
//AppContext.js
import { createContext } from 'react';
export const AppContext = createContext(null);
//Authenticated.js
import { useState } from 'react';
import { AppContext } from '#/src/AppContext';
export default function Authenticated()
{
const [message, setMessage] = useState('message');
return (
<AppContext.Provider value={{ message, setMessage }}>
<FlashMessage />
</AppContext.Provider>
);
}
//FlashMessage.js
import { useContext, useState} from 'react';
import { AppContext } from '#/src/AppContext';
const export default FlashMessage = () =>
{
const { message } = useContext(AppContext);
return (
<div>{message}</div>
);
}

In reactjs I am not able to pass variable through context api

I am trying to pass variables from a file in react js that is context.js to navbar.js but react.js warning says ProductConsumer was defined but never used and it keeps giving me those variables as undefined when I console.log() them.
The variable that I am trying to pass is "isAuthToCreateGym".
here is my context.js
import React, { Component } from "react";
const ProductContext = React.createContext();
class ProductProvider extends Component {
isAuthToCreateGym = () => {
console.log("vaibhav");
return true;
};
render() {
return (
<ProductContext.Provider
value={{
isAuthToCreateGym: this.isAuthToCreateGym
}}
>
{this.props.children};
</ProductContext.Provider>
);
}
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer};
and here is my navbar.js(part of it where I defined ProductConsumer
<ProductConsumer>
{value=>{
console.log("value :"+value);
return value;
}}
</ProductConsumer>

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.

Why am I getting "useContext is not a function" or "context is not defined"?

I am trying to use context in my stateless component. I updated my react to v16.8.0 and added useContext, however, I keep getting these two errors and don't know what else to do. Here is my code:
import React, { useState } from "react";
import axios from "axios";
import { LanguageContext } from "./languageContext";
import { useContext } from "react";
function StripeButton() {
const context = useContext(LanguageContext);
const stripe = Stripe("pk_live_5PjwBk9dSdW7htTKHQ3HKrTd");
const [error, setError] = useState();
const handleClick = () => {
stripe
.redirectToCheckout({
...
});
};
return (
<div>
<button
id="UrgentCheckedButtonYES"
className="btn-primary"
onClick={handleClick}
>
{this.context.main.name}
<br />
</button>
<div>{error}</div>
</div>
);
}
export default StripeButton;
StripeButton.contextType = LanguageContext;
You need to import useContext like this:
import { useContext } from 'react';
const { useContext } = React
useContext is exported as method property of React
(Tested this in React 18.)
In App.js File:
import { createContext } from "react";
import ChildComponent from "./components/ChildComponent";
export const Context = createContext("Default Value");
export default function App() {
const value = "My Context Value";
return (
<Context.Provider value={value}>
<ChildComponent />
</Context.Provider>
);
}
In ChildComponent.jsx file:
import { useContext } from "react";
import { Context } from "../App";
function ChildComponent() {
const value = useContext(Context);
return <h1>{value}</h1>;
}
export default ChildComponent;
You can have as many consumers as you want for a single context. If the context value changes,then all consumers are immediately notified and re-rendered.
If the consumer isn't wrapped inside the provider, but still tries to access the context value (using useContext(Context) or <Context.Consumer>), then the value of the context would be the default value argument supplied to createContext(defaultValue) factory function that had created the context.

Resources