I have a list of user on a page , what i want to achieve is render user detail when i click on the link with his id. for that i have tried to send the id and retrieve the data with a new request like this :
My routes :
<Route path='/coupeurs' element={<Coupeurs />} />
<Route path='/coupeurs/add' element={<AddCoupeur />} />
<Route path='/coupeurs/:id' element={<Coupeur />} />
My list :
<ul>
{coupeurs.map(coupeur => (
<li key={coupeur.id}>
{coupeur.nom} {coupeur.prenom}
<div className='coupeur-actions'>
<Link to={`/coupeurs/${coupeur.id}`}>Voir</Link>
<Link to={`/coupeurs/${coupeur.id}/edit`}>Editer</Link>
</div>
</li>
))}
</ul>
the place where i want display details :
export default function Coupeur(props) {
console.log(props);
let { id } = useParams();
const [coupeur, setCoupeur] = useState(getCoupeurInfoById(id));
console.log(coupeur);
return (
<div>
<h3>Identifiant : {id}</h3>
<h3>Nom : {coupeur.nom}</h3>
<h3>Prenom : {coupeur.prenom}</h3>
<h3>Statut : {coupeur.statut}</h3>
</div>
)
}
My getCoupeurInfoById function :
export async function getCoupeurInfoById(id) {
const docRef = doc(db, "coupeurs", id);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
return docSnap.data();
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}
i get my page without the info , but when i console log my variable i can see the result !
Can you help me found the right way to do that kind of things ? thank you
Since getCoupeurInfoById is an async function it implicitly returns a Promise object and subsequently (quickly) resolves and mutates the coupeur state variable. When you log it you see the resolved value.
The useState hook expects a synchronous function that returns the initial state value.
Use a useEffect hook to handle the side-effect of fetching/computing the coupeur value.
Example:
export default function Coupeur(props) {
const { id } = useParams();
const [coupeur, setCoupeur] = useState(); // <-- initially undefined
useEffect(() => {
const getCoupeur = async () => {
try {
const coupeur = await getCoupeurInfoById(id);
setCoupeur(coupeur);
} catch(error) {
// handle any rejections/errors/etc
}
};
getCoupeur(); // <-- fetch/compute coupeur value
}, [id]);
if (!coupeur) return null; // or loading indicator/etc
return (
<div>
<h3>Identifiant : {id}</h3>
<h3>Nom : {coupeur.nom}</h3>
<h3>Prenom : {coupeur.prenom}</h3>
<h3>Statut : {coupeur.statut}</h3>
</div>
)
}
I have an application in which you can register and log in. I store all users in useState, which is located in App.js (initially there are three users). If a new user registers, then I store his data in localStorage, so that when the application is restarted, they would be in my UseState App.js, this is the problem.
If you work in one session, then everything works well: After registrations, I can easily log into a new account, regardless of how many I have created (Data is written correctly in localStorage), but as soon as I restart the application, then in my App.js from localStorage only one LAST object from localStorage comes in, although if I display the actions of my useEffect in the console, then
From localStorage, all users are moved one by one, and as a result, only the last user is added to useState, can you tell me what could be the problem?
Here code:
App.js (Error in useEffect i think)
import React, {useState, useEffect} from 'react';
import './App.css';
import {Routes, Route} from 'react-router-dom'
import Registr from './components/AuthPage/Registr';
import Login from './components/AuthPage/Login';
function App() {
const [activeUser, setActiveUser] = useState();
const [realUsers, setRealUsers] = useState([]);
const [users, setUsers] = useState(
[
{
id: 0,
name: 'Andrey',
email: 'qwerty#mail.ru',
password: 'qwerty'
},
{
id: 1,
name: 'Roma',
email: 'ramenCisco#mail.ru',
password: '123'
},
{
id: 2,
name: 'Ilya',
email: 'ilyazxc#mail.ru',
password: 'zxc'
}
]
)
useEffect(() => {
for (let i = 0; i < localStorage.length; i++) { //
const userKey = localStorage.key(i); //find user key name
const JSONUserFromLocalStorage = localStorage.getItem(userKey);
const parsedUser = JSON.parse(JSONUserFromLocalStorage);
setUsers([...users, parsedUser])
}
}, []);
return (
<>
<Routes>
<Route path = '/' element = {<Login users = {users} setActiveUser = {setActiveUser}/>}></Route>
<Route path = '/registration' element = {<Registr users = {users} setUsers = {setUsers}/>}></Route>
</Routes>
</>
);
}
export default App;
Login.js
import React, {useReducer, useState} from 'react';
import closedEye from '../icons/closedEye.png';
import openedEye from '../icons/openedEye.png';
import warning from '../icons/warning.png';
import './Login.css';
import {Link} from 'react-router-dom';
function Login({users, setActiveUser}){
const [anError, setStatus] = useState()
const [isPrivate, setPrivate] = useState(true);
let typeOfEye = isPrivate ? closedEye : openedEye;
let typeOfInput = isPrivate ? 'password' : 'text';
const [form, setForm] = useState({
email: '',
password: ''
});
const changeHandler = (e) => {
setForm({...form, [e.target.name] : e.target.value});
}
const checkForValidate = () =>{
const {email, password} = form;
if (email.length === 0){
setStatus(
<div className='error-block'><img src = {warning} alt = 'warning'></img><p className='error'>Enter your email</p></div>
);
return
}else{
setStatus()
}
if (password.length === 0){
setStatus(
<div className='error-block'><img src = {warning} alt = 'warning'></img><p className='error'>Enter your password</p></div>
);
return
}else{
setStatus()
}
//Checking if a user exists in the database by email
const doesEmailValid = users.find(user => user.email === email)
if(!doesEmailValid){ //doesEmailValid the user object we found by email setStatus(
<div className='error-block'><img src = {warning} alt = 'warning'></img><p className='error'>Uncorrect email</p></div>
);
return
}else{
setStatus()
}
if (doesEmailValid.password !== password){
setStatus(
<div className='error-block'><img src = {warning} alt = 'warning'></img><p className='error'>Uncorrect password</p></div>
);
}else{ //If everything is alright:
setStatus(<p className='succes'>User successfully authorized</p>);
setActiveUser(doesEmailValid);
}
}
return (
<div className='login-wrapper'>
<div className = 'login'>
<h3>authorize</h3>
{anError}
<form
onSubmit={(e) => e.preventDefault()}>
<div className='form'>
<div className='inputs'>
<div className='email-field'>
<label htmlFor='email'>Email</label>
<input
type = 'email'
name = 'email'
onChange = {changeHandler}
>
</input>
</div>
<div className='password-field'>
<label htmlFor='email'>Password</label>
<div className='row'>
<input
type = {typeOfInput}
name = 'password'
maxLength={14}
onChange = {changeHandler}
>
</input>
{/* <img src = {require('./closedEye.png')}></img> */}
<img
src = {typeOfEye} alt = 'eye'
onClick={()=> {setPrivate(!isPrivate)}}
></img>
</div>
</div>
</div>
<div className='login-buttons'>
<button
type = 'submit'
onClick={checkForValidate}
>Enter</button>
<Link to = '/registration'>No account?</Link>
</div>
</div>
</form>
</div>
</div>
)
};
export default Login;
If we created one account in the LAST session, then authorization is successful.
If we have created several accounts, then we can only log in to the LAST created one, otherwise my error comes out: Uncorrect email
Quickly looking at your code it seems that setUsers([...users, parsedUser]) is going to be an issue. React does batch setState updates every 10ms or so, which means that your entire loop will have executed over and over before it ever actually sets the state. That's why it will only set the last item that was ever called before the state update happens.
Instead do something like:
users.push(JSON.parse(// user))
Then:
setUsers(users)
So you can set all of them at once.
This doesn't answer your question, but I want to call out a security issue with your code. It looks like you're storing passwords in the database, which you should never do. You also shouldn't store them in local storage.
You want to store a password hash in the database, and hash the password every time on login to check if it's the correct password. See here for some more info.
This is my first time using firestore and I have a react app where I want to display the logged user's username. I try to fetch the username from a firestore DB where I query users using the email of the user.
firebase.js
export const GetUserName = (email)=>{ //get single doc by email
let name = []; //store the name
//this is where the problem begins
db.collection('users').where("Email", "==" ,email)
.get()
.then(snapShot=>{
snapShot.forEach((snap)=>{
name.push(snap.data().userName); //stores only in array temporarily
console.log(name) //shows correct array
});
})
.catch(err =>{
console.log(err);
})
console.log(name); //shows empty array
return name[0]; //array is empty
}
then I try to display the username in another component I have
profilePage.js
import {GetUserName} from '../firebase';
export default function ProfilePage(){
const {user , logout} = useAuth(); //this is access to user.email
return (
<div>
<h1>Hi {getUsername(user.email)} </h1>
<Button onClick={handleLogout} color = "primary" variant = "outlined">Log out</Button>
</div>
);
}
But I only get "hi" without the username
Since the user name is read from Firestore asynchronously by the time your return name[0] runs, the name.push(snap.data().userName); hasn't run yet. You can easily check this by setting breakpoints on these lines and running the code in a debugger.
In situations like this you need to pass the data to the rendering code by putting it in the component's state. Using the useState hook that'd be:
import {GetUserName} from '../firebase';
export default function ProfilePage(){
const {user , logout} = useAuth(); //this is access to user.email
const [userName, setUsername] = useState();
useEffect(() => {
db.collection('users').where("Email", "==" ,user.email)
.get()
.then(snapShot=>{
snapShot.forEach((snap)=>{
setUsername(snap.data().userName);
});
})
.catch(err =>{
console.log(err);
})
});
return (
<div>
<h1>Hi {userName} </h1>
<Button onClick={handleLogout} color = "primary" variant = "outlined">Log out</Button>
</div>
);
}
So i've basically got 2 components on my page.
First is the search component where the users need to type their username and second one where their stats get displayed
and here is my API request call in App.js
useEffect(()=>{
const fetchStats = async ()=> {
const result = await axios.get(`https://cors-anywhere.herokuapp.com/https://public-api.tracker.gg/v2/csgo/standard/profile/steam/${username}`,
{
headers: {
'TRN-Api-Key' : '***************************',
}
}
)
if(username !== null){
console.log(result.data)
setStats(result.data)
}
}
fetchStats()
},[username])
and this is the search component
const Search = ({setInputText, setUsername, inputText, username}) => {
const inputHandler = (e)=> {
setInputText(e.target.value)
}
const searchHandler = (e)=> {
e.preventDefault()
setUsername(inputText)
}
return (
<div>
<form>
<input value={inputText} onChange={inputHandler} type="text"/>
<button onClick={searchHandler}>Search</button>
</form>
</div>
)
}
What i'm having an issue with is when i click a button in the username component the value(username) from the form gets stored in the 'username' state in App.js. Now i'm using this code in the stats component.
const Stats = ({stats}) => {
return (
<div>
<h1>{stats.data.platformInfo.platformUserHandle}</h1>
</div>
)
}
export default Stats
Here stats.data.platformInfo.platformUserHandle doesn't exist when the app starts so it gives me a LOT of errors. How do i keep the app from crashing till the user has input something and data can be sent to the stats component?
I'm using the npm module aws-cognito-next to implement authentication for one of my app. There the function getServerSideAuth(context.req); seems to not working as expected:
export async function getServerSideProps(context) {
// getServerSideAuth will parse the cookie
const initialAuth = getServerSideAuth(context.req);
return { props: { initialAuth } };
}
Then in the same page which is Home (/pages/home.js) I use this returned initialAuth as follows:
const Register = ( {initialAuth} ) => {
console.log("initialAuth is: " + util.inspect(initialAuth))
const auth = useAuth(initialAuth);
...
//other logic
...
}
But I get out put as: initialAuth is: null. So why doesn't initialAuth isn't getting returned from serverside? What am I doing wrong here?
getServerSideAuth checks to see if user is logged in and returns tokens that can be used in useAuth. You can check for null and add logic to send the user to the login/logout pages.
const Register = ( {initialAuth} ) => {
const auth = useAuth(initialAuth);
const { login, logout } = useAuthFunctions();
return (
<React.Fragment>
{auth ? (
<button type="button" onClick={() => logout()}>
sign out
</button>
) : (
<React.Fragment>
<button type="button" onClick={() => login()}>
sign in
</button>
</React.Fragment>
)}
</React.Fragment>
);
}
For more information on useAUth see here.