I am setting up firebase user authentication for a reactjs app. Everything's been working so far but when I write const {signup} = useAuth() in the file that actually renders the login page, the page turns blank.
There is a context file set up with:
import React, { useContext, useState, useEffect} from 'react'
import {auth} from '../firebase'
const AuthContext = React.createContext()
function useAuth() {
return useContext(AuthContext)
}
function AuthProvider({children}) {
const[currentUser, setCurrentUser]= useState()
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email,password)
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
})
return unsubscribe
},[]);
const value = {
currentUser,
signup
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
export {useAuth, AuthProvider}
With my RenderPage everything is working correctly, it's just a basic JSX/HTML return function with
import {useAuth, AuthProvider} from '../Contexts/AuthContext'
import React, { useState, useRef } from "react";
function RenderPage() {
return(
<AuthProvider>
<h1>Generic text</h1>
<AuthProvider>)}
export default RenderPage;
And everything works perfectly,
But then when I put in the line const {signup} = useAuth() to make it:
import {useAuth, AuthProvider} from '../Contexts/AuthContext'
import React, { useState, useRef } from "react";
function RenderPage() {
const {signup} = useAuth()
return(
<AuthProvider>
<h1>Generic text</h1>
<AuthProvider>
);
The page turns blank.
Does anyone have any idea why this might be? (I already checked the firebase API key and set it up on the realtime database)
Thanks in advance
Related
So I was following this tutorial to use global state through react context api.
I want to change the navbar based on if the user is logged in, where I need to use global state. But I am getting a white screen so I assume I did something wrong.
AppContext.js
import React from "react";
// ./components/AppContext.js
const AppContext = React.createContext();
export default AppContext;
Index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import { CookiesProvider } from 'react-cookie';
import App from "./App";
import AppContext from "./components/AppContext";
// index.js
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<CookiesProvider>
<App/>
</CookiesProvider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
reportWebVitals();
App.js
import React, {useState} from 'react';
import './App.css';
import Home from './Home';
import {BrowserRouter as Router, Route, Routes} from 'react-router-dom';
import User from "./User";
import NavBarSI from "./components/NavBarSignedIn";
import NavBarGL from "./components/NavBarGeneral";
import AppContext from "./components/AppContext";
//App.js
const App = () => {
const userSettings = {
setting2name: setting2value,
setSetting2value
};
const [setting2value, setSetting2value] = useState(false);
const navbar = setting2value ?
<NavBarSI/> : <NavBarGL/>
return (
<Router>
<AppContext.Provider value={userSettings}>
{navbar}
<NavBarSI/>
<div>
<Routes>
<Route exact path="/" element={<Home/>}/>
<Route exact path="/user" element={<User/>}/>
</Routes>
</div>
</AppContext.Provider>
</Router>
);
}
export default App;
Home.js example where I change the boolean to true when user logs in.
import React, {useContext, useEffect, useState} from 'react';
import './App.css';
import { Button, Container } from 'reactstrap';
import { useCookies } from 'react-cookie';
import AppContext from "./components/AppContext";
// Home.js
const Home = () => {
const myContext = useContext(AppContext);
const [authenticated, setAuthenticated] = useState("authenticated");
const [loading, setLoading] = useState(false);
const [user, setUser] = useState(undefined);
const [cookies] = useCookies(['XSRF-TOKEN']);
useEffect(() => {
setLoading(true);
fetch('/user', { credentials: 'include' })
.then(response => response.text())
.then(body => {
if (body === '') {
setAuthenticated(false);
} else {
setUser(JSON.parse(body));
setAuthenticated(true);
myContext.setSetting2value(true);
}
setLoading(false);
});
}, [setAuthenticated, setLoading, setUser])
const login = () => {
let port = (window.location.port ? ':' + window.location.port : '');
if (port === ':3000') {
port = ':8080';
}
window.location.href = `//${window.location.hostname}${port}/private`;
}
const logout = () => {
fetch('/user/logout', {
method: 'POST', credentials: 'include',
headers: { 'X-XSRF-TOKEN': cookies['XSRF-TOKEN'] }
})
.then(res => res.json())
.then(response => {
window.location.href = `${response.logoutUrl}&returnTo=${window.location.origin}`;
});
}
const message = user ?
<h2>Welcome, {user.name}!</h2> :
<p>Please log in to manage your JUG Tour.</p>;
const button = authenticated ?
<div>
<br/>
<Button color="link" onClick={logout}>Logout</Button>
</div> :
<Button color="primary" onClick={login}>Login</Button>;
if (loading) {
return <p>Loading...</p>;
}
return (
<div>
<Container fluid>
{message}
{button}
</Container>
</div>
);
}
export default Home;
Check your console.
You getting white screen because trying to assign variable before its declaration.
Swap variables.
Should do the trick
const [setting2value, setSetting2value] = useState(false);
const userSettings = {
setting2name: setting2value,
setSetting2value
};
Furthermore i suggest to memoize your context object to prevent additional re-renders using useMemo.
Like this:
const context = useMemo(() => {
const ctx = { setting2name: setting2value,setSetting2value}
return ctx;
}, [setting2name]);
Hope it helps!
I can't change the value of the state of the store using the reducer. I'm making an app which has a login-functionality. When a person opens up the app, depending on the fact if he is logged in or not, it should show the right screen. The problem I'm having right now is it doesn't seem to be able to change the store state out of another screen. Anybody who can help me?
import {createStore} from "redux";
const initialState = {
value: false
}
function reducer(state= initialState, action) {
const newState = {...state};
if(action.type === 'login') {
console.log("hahaha you logged in");
newState.value = true;
}
else if(action.type ==='logout') {
console.log("hahaha you logged out")
newState.value = false;
}
return newState;
}
const store = createStore(reducer);
export default store;
This is the store, this should change the value accordingly.
When the login button is pressed on loginscreen it should call the reducer function.
import React, { useRef, useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Image, Dimensions, AsyncStorage } from 'react-native';
import axios from "axios";
import store from "../routes/store"
function LoginScreen({navigation}, props) {
const win = Dimensions.get('window');
const [email,setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const [errMsg, setErrMsg] = useState('');
const logInCheck = async (e) => {
console.log("Ingelogd");
store.dispatch({type: 'login'})
}
return(
<Root>
<View style={styles.container}>
<TouchableOpacity style={styles.loginBtn} onPress{logInCheck}>
<Text style={styles.loginText}>LOGIN</Text>
</TouchableOpacity>
</View>
</Root>
)
}
This is the code which should render the right screen depending on the fact if the person is logged in!
import React, { useState, useReducer } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState()? (
<AuthStack.Screen name="Tabs" component={Tabs} options={{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
store.subscribe(rendFunc);
};
export default AuthStack;
The problem with your code is in react re-render rather than the redux store not updating. The store is updating properly but your react component is not aware of any change that has occurred in the store so no re-render is happening.
Firstly you need to add subscriptions to the redux store listener in useEffect i.e. when the component is mounted and later unsubscribe to prevent memory leakages. The redux subscribe function takes in a function to handle whenever state change has occurred in the redux store.
In this function, you can create a state using useState to create a re-render of the component.
use the below code in the authstack and it should work fine.
if any more queries you can contact me # wizcoderzcorp#gmail.com
import React, { useState, useReducer, useEffect } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-
stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
const [loginState, setLoginState] = useState(false)
const handleReduxStateChange = () =>{
setLoginState(store.getState().value)
}
useEffect(()=>{
const unsubscribe = store.subscribe(handleReduxStateChange);
return unsubscribe()
},[])
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState().value? (
<AuthStack.Screen name="Tabs" component={Tabs} options=
{{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
};
export default AuthStack;
I'm trying to wrap all components under the app in a context to provide the things that I want
(as You can see it's my UserContext component)
enter code here
import React, { useState, createContext, useContext } from 'react'
const Context = createContext();
export let useUserContext = () => useContext(Context);
export default function UsersContext({ children }) {
const [users, setUsers] = useState();
const createUser = (user) => {
if (!user.name || !user.email || !user.password) return;
const newUsers = [...users, user];
setUsers(newUsers);
}
return (
<Context.Provider value={{ users, createUser }}>
{children}
</Context.Provider>
)
}
(it is my app component)
enter code here
import Nav from "./components/nav/Nav";
import Container from "./components/container/Container";
import { BrowserRouter } from "react-router-dom";
import UsersContext from "./components/contexts/UserContext";
function App() {
return (
<UsersContext>
<BrowserRouter>
<Nav />
<Container />
</BrowserRouter>
</UsersContext>
);
}
export default App;
It's used to be like this in my projects and I didn't have any problem but now
the error I'm getting "TypeError: (destructured parameter) is undefined" also says that it's because of the children in UserContext In my opinion it shouldn't happen maybe you can help me to find the problem I can't see.
Try: <Context.Provider value={[ users, { createUser } ]}>
instead of: <Context.Provider value={{ users, createUser }}>
edit:
also might try:
instead of
const newUsers = [...users, user];
setUsers(newUsers);
do
setUsers((currentUsers) => [...currentUsers, user]);
I found the problem. it was because of the useState. it was undefined and I was calling the property that related to useState at the UserContext.
Heres my simplified React component:
import React from "react"
import {useSelector, useDispatch} from "react-redux"
import { useRouteMatch } from "react-router-dom"
import gameService from "../services/game"
import {setGame} from "../reducers/game" //action creator
const Game = () => {
const game = useSelector(state => state.game)
const dispatch = useDispatch()
const match = useRouteMatch('/games/:id')
gameService.oneGame(match.params.id).then(g => dispatch(setGame(g)))
const gameInfo = (game) => {
...some proceessing
return(JSX containig info about the game)
}
return(
game ? <div>
<h2>{game.name}</h2>
{gameInfo(game)}
</div> :
loading
)
}
export default Game
Component is called from App.js:
<Route path="/games/:id">
<Game />
</Route>
Everything works but the site renders infinitely. Does the promise resolve after the component has rendered and this renders the component again, or what is happenig here? And what is the easiest fix?
I think you may want to put the call to gameService in a useEffect hook so that it is only called when match.params.id and dispatch change rather than every time the component is re-rendered.
Try amending it to be:
import React, { useEffect } from "react"
import {useSelector, useDispatch} from "react-redux"
import { useRouteMatch } from "react-router-dom"
import gameService from "../services/game"
import {setGame} from "../reducers/game" //action creator
const Game = () => {
const game = useSelector(state => state.game)
const dispatch = useDispatch()
const match = useRouteMatch('/games/:id')
useEffect(() => {
gameService.oneGame(match.params.id).then(g => dispatch(setGame(g)))
}, [match.params.id, dispatch]);
const gameInfo = (game) => {
...some proceessing
return(JSX containig info about the game)
}
return(
game ? <div>
<h2>{game.name}</h2>
{gameInfo(game)}
</div> :
loading
)
}
export default Game
I'm having trouble getting my child component to rerender when using a context provider. Logging the listener output shows that the AuthContextProvider component does successfully update and connect to the authentication service, but the child App component does not rerender - the screen shows that I am not logged in.
Any help would be greatly appreciated.
AuthContext.tsx:
import React, {createContext, useEffect, useState} from 'react';
import firebase from 'firebase';
import firebaseConfig from '../config/firebaseConfig';
export type AuthContextProps = {
user: firebase.User | null;
authenticated: boolean,
setUser: any,
loadingAuthState: boolean
}
const defaultAuthContext: AuthContextProps = {
user: null,
authenticated: false,
setUser: null,
loadingAuthState: true
}
let AuthContext = createContext(defaultAuthContext);
const AuthProvider = (props: any) => {
const AuthContext = React.createContext<Partial<AuthContextProps>>({});
const [user, setUser] = useState(null as firebase.User | null);
const [loadingAuthState, setLoadingAuthState] = useState(true);
useEffect(() => {
const fetchUser = async () => {
firebase.initializeApp(firebaseConfig).firestore();
await firebase.auth().signInWithEmailAndPassword('test#gmail.com', 'gympass').then(val => val.user);
}
fetchUser().then(() => {
firebase.auth().onAuthStateChanged((user: any) => {
setUser(user)
console.log('LOGGED IN')
setLoadingAuthState(false)
});
});
}, [])
const value = {
user,
authenticated: user !== null,
setUser,
loadingAuthState
}
console.log('VALUE:', value)
return (
<AuthContext.Provider value={value}>
{props.children}
</AuthContext.Provider>
)
}
const AuthConsumer = AuthContext.Consumer;
export {AuthContext, AuthProvider, AuthConsumer}
App.tsx:
import React, {useContext, useEffect, useState} from 'react';
import {AuthContext} from './context/AuthContext';
import {AuthContextProps} from "./context/AuthContext";
function App() {
const auth: AuthContextProps = useContext(AuthContext)
return (
<div className="App">
<header className="App-header">
<p>You are currently {auth.user === null ? 'not ' : ''}logged in.</p>
</header>
</div>
);
}
export default App;
index.tsx:
import React, {useEffect, useState} from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {AuthProvider} from './context/AuthContext'
ReactDOM.render(
<React.StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();