In my react application I am trying to use context api. In my component I am importing the context but it is giving error that object can not destructure the property. I am trying to implement cart functionality in my app. I am using hooks.
ImgContext.js
import React, { createContext, useState } from 'react';
const ImgContext = createContext();
const ImgConProvider = ({children}) => {
const [myCart, setMyCart] = useState([]);
return(
<ImgContext.Provider value={{myCart, setMyCart}}>
{children}
</ImgContext.Provider>
)
}
export {ImgContext, ImgConProvider}
ImageGrid.js
import React, { useContext, useState } from 'react';
import ImageGrid from './ImageGrid';
import { ImgContext } from './Context/ImageContext';
const Home = () => {
const { myCart } = useContext(ImgContext);
return (
<div className="App">
{myCart}
</div>
)
}
export default Home;
You are not providing a a default value when creating the context. If there is a scenario where the component doenst have access to a provider the value from context would be undefined which maybe causing the issue.
Better provide a default value.
const ImgContext = createContext({});
Related
i have been trying to implement a context api solution since i want to use children states(data) in my app.js without lifting up the states. anyways i have tried to implement it a context api soloution to by doing the following :
i created a folder names context and then created Context.js
the code is as follows:
mport { createContext,useState } from "react";
export const Mycontext = createContext()
const Context = ({children}) =>{
const [post, setPost] = useState([])
return(
<Mycontext.Provider value={[post,setPost]}>
{children}
</Mycontext.Provider>
)
}
export default Context
i wrapped the index.js file with the Provider wrapper as follows:
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import Context from './context/Context';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Context>
<App />
</Context>
);
my main goal for now is to use useState hook data or states so i can use them in higher up comonents , in this case i want my post.js file to change usestate data in context so i can then use that data to post something in App.js using a container component that takes value as a props
i will post the both post.js and container.js and app.js below
import React,{useContext,useState,useEffect,useRef} from 'react'
import '../HomeMainStyling/HomeStyling.css'
import Tweets from './Tweets'
import Context from '../../context/Context'
function Tweet() {
const tw = useRef('')
const {post,setPost} = useContext(Context);
useEffect(() => {
if (post.length) console.log(post);
}, [post]);
function PostNow(event){
event.preventDefault();
setPost((oldpost) => [tw.current.value,...oldpost]);
}
return (
<div className="tweetcontainer">
<textarea ref={tw} className="tweetinfo"></textarea>
<button className="postIt" onClick={PostNow}>tweet</button>
</div>
)
}
export default Tweet
//
the container is the following:
import React from 'react'
import '../HomeMainStyling/HomeStyling.css'
function Tweets({value}) {
return (
<h2>{value}</h2>
)
}
export default Tweets
App.js:
import Tweet from './Center/HomeMain/Tweet';
import Tweets from './Center/HomeMain/Tweets';
import { useContext,useState } from 'react';
import Context from './context/Context';
function App() {
const {post,setPost} = useContext(Context);
return (
<div className="App">
<Tweet/>
<Tweets value={post}/>
</div>
);
}
export default App;
the app should in principle post 1 h1 element for every click in Tweet components
The useContext hook takes the context you created using createContext() as a parameter, but you are passing a custom component to it, so try:
import { Mycontext } from './context/Context';
const [post, setPost] = useContext(Mycontext)
<Mycontext.Provider value={[post,setPost]}>
this is wrong you ahve to write
<Mycontext.Provider value={{post,setPost}}>
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
I have navigation constant which is an array of objects(web-store mega-nav). I need to use context provider and when I'm trying to use my context it's telling me NavContext' is not defined no-undef.
NavContext.js
import { createContext } from 'react'
const navigation = [...] // array of objects
const NavContext = createContext(navigation)
export default NavContext
Nav.js
import {createContext} from 'react'
import NavContext from './context/NavContext' //added
function Nav() {
return (
<NavContext.Provider> //deleted value
// childrens
</NavContext.Provider>
)
}
Sidebar.js
//then in one of the child I'm trying to call it:
import { useContext } from 'react'
import NavContext from '../context/NavContext' //added
function Sidebar(){
const nav = useContext(NavContext)
return (
{nav.map(...)} // nav is undefined
)
}
Now nav constant is undefined when I'm using useContext
You need to export the create context like this
export const NavContext = createContext(navigation)
Then import it into your child component like this
import { NavContext} from "../Nav";
//Create a new NavContext.js File.
import React, { createContext, useReducer } from "react";
export const NavContext = createContext();
const initialState = {
}
function reducer(state, action) {
return { ...state, ...action };
}
export const NavProvider = (props) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<NavContext.Provider value={{ state, dispatch }}>
{props.children}
</NavContext.Provider>
);
};
Then in your index.js file.
import { NavProvider} from "./NavContext";
ReactDOM.render(
<NavProvider>
<App />
</NavProvider>,
document.getElementById("root")
);
If that doesnt work idk what will.
Try not to pass any arguments into createContext, then pass navigation into Context provider as prop
<Context.Provider value={navigation} />
And then get the value using useContext Hook in your consumer component
For my knowledge, you have to import useContext like this.
import React, { useContext } from 'react'
I am trying to get Redux state out of the global store and when I use useSelector in a WithAuth HOC I am not getting the proper state. On my global state there are two keys: currentUser and currentUser.isAuthenticated.
In my Redux dev tools, the isAuthenticated key shows having the value true, but when I make use of the hook, I get false.
I've had a look at the official documentation, but I couldn't find anything which might help me, if I've read it correctly.
Could someone tell me what I'm doing wrong? I am new to using hooks for Redux. Thanks.
withAuth.tsx
import React,
{ useEffect } from "react";
import { useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
import { Routes } from "../../constants/RoutesNames";
import { DefaultRootState } from "../../store/reducers";
interface Props
{
ComponentToBeRendered: React.FC<unknown>
props?: any
};
const stateSelector = (state: DefaultRootState) => state.currentUser.isAuthenticated;
const WithAuth : React.FC<Props> = ({ ComponentToBeRendered, props }) =>
{
const isAuthenticated = useSelector<DefaultRootState, boolean>(stateSelector);
const history = useHistory();
useEffect(() =>
{
if (!isAuthenticated) history.push(Routes.SIGN_IN);
}, [ isAuthenticated, history ]);
return (
<ComponentToBeRendered {...props} />
)
}
export default WithAuth;
What do you mean by the wrong value? Could you add more info to the problem?
Also remember you can shorthand the return of a statement like this to keep code cleaner:
const stateSelector = (state: DefaultRootState) => state.currentUser.isAuthenticated
It turns out the useSelector hook does a double take on the Redux state. If you console.log (console.log("Got isAuth", isAuthenticated);) inside the useEffect hook you will see that the first log will be Got isAuth false and the second Got isAuth true.
I don't know if that's the whole story, but for now I've re-implemented WithAuth to accept a rendered element rather than a component to be rendered. And then I conditionally return that element or my sign in page rendered. Thus,
import React from "react";
import { useSelector } from "react-redux";
import AuthFlow from "../../pages/AuthFlow";
import { DefaultRootState } from "../../store/reducers";
interface Props
{
ComponentToBeRendered: React.ReactElement;
};
const isAuthenticatedSelector = (state: DefaultRootState) => state.currentUser.isAuthenticated;
const WithAuth : React.FC<Props> = React.memo(({ ComponentToBeRendered }) =>
{
const isAuthenticated = useSelector<DefaultRootState, boolean>(isAuthenticatedSelector);
return (
isAuthenticated ? ComponentToBeRendered : <AuthFlow />
)
})
export default WithAuth;
I don't know if this is the best approach, but I've found a workaround for now. Strange that useSelector can't just get the Redux state correctly on the first run. Maybe I'm missing something someone can show me.
=========== EDIT ===========
I thought a little more about this and I think this approach might be slightly better for various reasons
import React from "react";
import { useSelector } from "react-redux";
import BasicTextLink from "../../components/BasicLink/BasicLink";
import { Routes } from "../../constants/RoutesNames";
import { DefaultRootState } from "../../store/reducers";
interface Props
{
ElementToBeShown: React.ReactElement;
};
const isAuthenticatedSelector = (state: DefaultRootState) => state.currentUser.isAuthenticated;
const WithAuth : React.FC<Props> = React.memo(({ ElementToBeShown }) =>
{
const isAuthenticated = useSelector<DefaultRootState, boolean>(isAuthenticatedSelector);
return (
isAuthenticated ? ElementToBeShown : <h2>You are not signed in. Please {
<BasicTextLink baseColor useHoverEffect darkHover to={Routes.SIGN_IN} underline={"hover"}>
sign in
</BasicTextLink>
} first</h2>
)
});
export default WithAuth;
I have been playing with React Context API but not sure how I am supposed to type-check using the PropTypes package, given the value is passed down to the child component (Module1) via useContext() rather than via traditional props.
I have tried to implement PropTypes in both the parent (App) and child (module1) components but it is returning an error. Thanks.
import './App.css';
import Module1 from './Module1.js';
import PropTypes from 'prop-types';
export const MyContext = React.createContext();
const App = () => {
const [age, setAge] = useState(22);
const increaseAge = () => {
return setAge( age -1)
};
return (
<MyContext.Provider value={
{
age: age,
increaseAge: increaseAge
}
}>
<Module1/>
</MyContext.Provider>
);
}
App.PropTypes ={
age: PropTypes.number
}
export default App;
==========================================================================================
import React, { useContext } from 'react';
import {MyContext} from './App.js'
import PropTypes from 'prop-types';
const Module1 = () => {
const myAge = useContext(MyContext);
return (
<div>
Your age is: {myAge.age}
<button onClick={myAge.increaseAge}> increase age </button>
</div>
)
}
Module1.PropTypes = {
MyAge: PropTypes.object
}
export default Module1;