Why is the function from the context not recognized? - reactjs

I'm trying to make an auth Context:
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
and a custom hook to get it:
import { useContext } from "react";
import AuthContext from "./AuthProvider";
const useAuth = () => {
return useContext(AuthContext);
}
export default useAuth;
And now when I try to use the setAuth function:
const Login = () =>{
const {setAuth} = useAuth();
...
setAuth({authResponce});
I get an error : Uncaught TypeError: setAuth is not a function .
I'm new to react and I don't understand the reason yet, so I'll be glad to help.

Context only works if the provider is farther up the component tree than the consumer. If there is no provider, then Login will receive the default value, and you set the default to an empty object when you wrote createContext({});. Since that object has no setAuth function, you get the error.
To fix this, make sure the auth provider is near the top of the tree, and login is inside it. Login doesn't need to be an immediate child of the provider, but it does need to be a descendant.
// Good
const App = () => {
return (
<AuthProvider>
<Login />
</AuthProvider>
)
}
// Also good
const App = () => {
return (
<AuthProvider>
<SomeOtherComponent />
</AuthProvider>
)
}
const SomeOtherComponent = () => {
return <Login />
}
// Bad
const App = () => {
return (
<div>
<AuthProvider/>
<Login/>
</div>
)
}

Related

React Context in Typescript

I am creating context to handle authentication in typescript app. I have written this AuthProvider.ts
/*-- Imports --*/
/*-- AuthContextType interface and defaults -- */
const AuthContext = createContext<AuthContextType>(contextDefaults);
export const useAuth = () => {
return useContext(AuthContext);
};
interface AuthProviderProps {
children: ReactNode;
}
const AuthProvider = ({ children }: AuthProviderProps) => {
const [user, setUser] = useState<User | null>(null);
const isLoggedIn = !!user;
const login = (data: User) => {
setUser(data);
};
const logout = () => {
setUser(null);
};
return (
<AuthContextType.Provider value={{ user, isLoggedIn, login, logout }}>
{children}
</AuthContextType.Provider>
);
};
export default AuthProvider;
Then I am wrapping App with this AuthProvider;
/* --- imports ---*/
const App = () => {
return (
<AuthProvider>
<div>App</div>
</AuthProvider>
);
};
export default App;
This code is giving errors and when I write same code in javascript app it is working fine.
AuthContext.ts Errors
App.js Error
Any help will be appreciated.
Thanks
Could it be that you've used: AuthContextType.Provider when you mean AuthContext.Provider?
AuthContextType is a Typescript type, not a react context object.
const AuthProvider = ({ children }: AuthProviderProps) => {
....
return (
<AuthContext.Provider value={{ user, isLoggedIn, login, logout }}>
{children}
</AuthContext.Provider>
);
};
Does this work?

Passing function into context in react-native using typescript

const AuthContext = React.createContext<any>({ currentUser: null, signUp: null});
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [ currentUser, setCurrentUser ] = useState<any>();
function signUp(email?: string, password?: string){
if (email && password && email.length > 3 && password.length > 5){
return auth.createUserWithEmailAndPassword(email, password);
}
return auth.signInWithPopup(googleAuthProvider);
}
useEffect(() => {
const unsubscriber = auth.onAuthStateChanged( user => {
setCurrentUser(user);
})
return unsubscriber;
}, [])
const value = {
currentUser,
signUp,
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
This is how i imported, deconstructed and used
import {useAuth} from "./{some-path}/AuthContext";
function SignUp() {
const { signUp } = useAuth();
function insideSomeFunction(){
signUp!();
}
return (
{simple react-nataive componenst to call insidesomefunction}
)
}
Problem is whenever I try to use signUp by importing useAuth and deconstructing it to get signUp function, it always give null, even tho I have provided the implementation in the AuthProvider function.
I am new to react-native and using context in react
undefined value is error prone. So, initialize user with null:
const [ currentUser, setCurrentUser ] = useState<any>(null);
Make sure that you are wrapping the All Childs in the AuthProvider component so we can access the context in our children.
Like:
<AuthProvider>
<Child />
</AuthProvider>
And using the useAuth context in the Child Component.
AuthContext implementation is correct. Make sure you wrapped the entire app with AuthProvider.
export default function App(): ReactElement {
return (
<AuthProvider>
// I belive your app has a navigator
<Navigator />
</AuthProvider>
);
}

How to use context with hooks for authentication?

I'm trying to use context for handling pieces of authentication in my app. I was running into issues because I was trying to call useContext outside of my Context.Provider, so I moved the logic to a child component of the provider.
Now I'm getting an error message TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) where I'm calling useContext in the child component. Is the issue really with getting the values from the context or something else?
In app.js
import AuthContextProvider from "./components/context/authContext";
import RegisterRoutes from "./components/routing/registerRoutes";
function App() {
return (
<AuthContextProvider>
<Route
exact
path="/register"
render={(props) => (
<RegisterRoutes {...props} />
)}
/>
</AuthContextProvider>
)
}
In my authContext.js
import React, { useState, useEffect, createContext } from "react";
export const AuthContext = createContext();
const AuthContextProvider = (props) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
//Auth API logic here//
const apiOptions = {
url: "users/is-verified",
method: "GET",
headers: {
token: localStorage.token,
},
};
async function isAuth() {
axios(apiOptions)
.then((response) => {
const resData = response.data;
resData === true ? setIsAuthenticated(true) : setIsAuthenticated(false);
})
.catch((error) => {
console.log(error.response);
});
}
useEffect(() => {
isAuth();
}, []);
return (
<AuthContext.Provider
value={[isAuthenticated, setIsAuthenticated, setAuth]}
>
{props.children}
</AuthContext.Provider>
);
};
export default AuthContextProvider;
In my registerRoutes.js
import React, { useContext } from "react";
import { Redirect } from "react-router-dom";
import Register from "../pages/register";
import AuthContext from "../context/authContext";
function RegisterRoutes(props) {
const [isAuthenticated, setAuth] = useContext(AuthContext);
return !isAuthenticated ? (
<Register {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
);
}
export default RegisterRoutes;
As the error says, the Context.Provider in authContext.js value is not iterable:
<AuthContext.Provider value={[isAuthenticated, setIsAuthenticated, setAuth]}>
The value passed to the provider needs to be an iterable value, in this case, a valid JSON object, instead of the array that you have provided. so, we change it to:
<AuthContext.Provider value={{isAuthenticated, setIsAuthenticated, setAuth}}>
Then you change the reference in registerRoutes.js to correctly consume the new structure:
const [isAuthenticated, setAuth] = useContext(AuthContext);
becomes
const { isAuthenticated, setAuth } = useContext(AuthContext);
Voila! Your Context.Provider value is iterable and you can consume it in your application.
I think this will help you. My solution for accessing data in the context is creating a custom hook.
//localState.js
import { createContext, useState, useContext } from 'react'
const LocalStateContext = createContext()
const LocalStateProvider = LocalStateContext.Provider
function LocalState({children}) {
const [someState, setSomeState] = useState('')
const defaultValues = {
someState, setSomeState
}
return <LocalStateProvider value={defaultValues}>
{children}
</LocalStateProvider>
}
function useLocalState() {
const all = useContext(LocalStateContext)
return all
}
export {LocalState, LocalStateContext, useLocalState}
With this code you can wrap your whole app in the LocalState component and access context values by using the new useLocalState hook. For example
import { useLocalState} from './localstate'
const SomeComponent = () => {
const { someState } = useLocalState()
return (
///...whatever you want
)
}
export default SomeComponent
I think your issue may be that you have put your default values in an array inside of the value object.

Use Context API and functional component as service in React

I have a context API:
import React, { createContext, useState } from "react";
const UserContext = createContext();
const UserContextProvider = (props) => {
const [userInfo, setUserInfo] = useState({});
return (
<UserContext.Provider value={{ userInfo, setUserInfo }}>
{props.children}
</UserContext.Provider>
)
};
export { UserContextProvider, UserContext };
and use it in App.js:
<UserContextProvider>
// Router,...
</UserContextProvider>
Well, I gonna use context API in component like a service:
import { UserContext } from "...";
function UserService() {
const { userInfo, setUserInfo } = useContext(UserContext);
const updateUserInfo = (newUserInfo) => {
setUserInfo(newUserInfo); // for example: {name:'x'}
}
return null;
}
Now I wanna use UserService inside a component without add <UserService /> ? How can I call UserService.updateUserInfo()?
Your don't need to userService.You can use UserContext directly inside App.js and access to its function but you must wrap App.js inside UserContextProvider like:
<UserContextProvider>
<App />
</UserContextProvider>
Or you can use HOC(higher order component) like:
const withUsersContext = (Comp) => {
return (props) => (
<UserContextProvider>
<Comp {...props} />
</UserContextProvider>
);
};
// then inside App.js:
...
export default withUsersContext(App)
Now inside App.js:
const { userInfo, setUserInfo } = useContext(UserContext);
Note: if you wanna use UserContext inside something like UserService you must write a custom hook for UserService(rules of react-hooks).
Custom hooks for UserService:
function useUserService() {
const { userInfo, setUserInfo } = useContext(UserContext);
const updateUserInfo = (newUserInfo) => {
setUserInfo(newUserInfo);
}
return { updateUserInfo };
}
How to use inside a component:
...
const { updateUserInfo }= useUserService();
...

How to set a value with useContext()?

I am trying to set my state in Context from within a nested child component, but it doesn't see my method, which I created in the context provider:
import React, { createContext, useState } from "react"
const Context = createContext({})
const Provider = ({ children }) => {
const [value, setValue] = useState(undefined)
return (
<Context.Provider
value={{
value,
loadValue: currentValue => {
setValue(currentValue)
},
}}
>
{children}
</Context.Provider>
)
}
export default Context
export { Provider }
In my child component I try to set it like so:
import React, { useContext } from "react"
import Context from "../context/value.context"
const MyPage = ({ data }) => {
const value = data.contentfulValue
const { loadValue } = useContext(Context)
loadValue(value)
return (
<Layout>
...
</Layout>
)
}
export default MyPage
export const valueQuery = graphql`
query valueBySlug($slug: String!) {
contentfulValue(slug: { eq: $slug }) {
...
}
}
`
The error I'm getting is TypeError: loadValue is not a function
While you not providing the entire app structure,
you may encounter such error when MyPage is not a child of Context.Provider,
therefore it suggested to add an initial value when creating the context:
The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing undefined as a Provider value does not cause consuming components to use defaultValue.
const Context = createContext({ value: undefined, loadValue: () => console.log('Default function') })
I have Changed your code a little bit
export context as named so that you can use it
import React, { createContext, useState } from "react"
export const Context = createContext({})
export default const Provider = ({ children }) => {
const [value, setValue] = useState(undefined)
return (
<Context.Provider
value={{
value,
loadValue: currentValue => {
setValue(currentValue)
},
}}
>
{children}
</Context.Provider>
)
}
Use static context like following.
import React, { useContext } from "react"
import {Context} from "../context/value.context"
const MyPage = ({ data }) => {
static contextType = Context
const value = data.contentfulValue
const { loadValue } = this.context
loadValue(value)
return (
<Layout>
...
</Layout>
)
}
export default MyPage
And Remember to import Provider at the topmost of the component tree so that any component can use it, like this
<Provider>
{/* <PageContent> */}
<Navbar />
<Forms />
{/* </PageContent> */}
</Provider>
if still doesn't work do tell me.

Resources