How to fetch data for specific User in Firebase/React - reactjs

Got my Landing page in here with hardcoded username / password into firebase db.
after successful login, I am redirected to the homepage. However, I am trying to figure out how to fetch the data for the specific logged in user. Currently, in my Firebase I have only 1 Collection which is Users and contains some documents any of the documents has their own fields it is all hardcoded for the sake of the test.
After the log in I am currently logging data but I only see the last added document. How do i attach the User to see its own data. I tried creating directly in firebase a document with the same UID as the logging user but the data that i am logging is still the last added document instead of the right for the specific user.
function LandingPage(props) {
const [showErrorModal, setShow] = useState(false);
const emailInputRef = useRef();
const passwordInputRef = useRef();
const navigate = useNavigate();
function sumbitForm(e) {
e.preventDefault();
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;
const url = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyCNBAxjeKNoAPPjBV0JW4vZ0QaTaOx9-L4';
fetch(url, {
method: 'POST',
body: JSON.stringify({
email: enteredEmail,
password: enteredPassword,
returnSecureToken: true,
}),
headers: {
'Content-Type': 'application/json',
},
}).then((res) => {
if (res.ok) {
navigate('/homepage')
} else {
setShow(true);
}
return res.json()
}).then((data) =>
console.log(data))
}
function handleClose() {
setShow(false)
}
return (
<div className='wrapper'>
<form onSubmit={sumbitForm}>
<h3>Login Here</h3>
<label htmlFor="username">Username</label>
<input type="text" placeholder="Sigh up with email" id="username" ref={emailInputRef} ></input>
<label htmlFor="password">Password</label>
<input type="password" placeholder="Password" id="password" ref={passwordInputRef}></input>
<button className='button' type="submit" typeof='submit' >Log In</button>
{showErrorModal ? <Modal show={showErrorModal} onHide={handleClose}
backdrop="static">
<Modal.Header>
<Modal.Title>Incorrect Username/Password</Modal.Title>
</Modal.Header>
<Modal.Body>
Please provide the correct credentials
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal> : null}
<div className='landingpage-logo'>
<img src={logo} className="landingpage-logo"></img>
</div>
</form>
</div>
)
}
export default LandingPage;
import React, { useEffect, useState } from "react";
import { getDatabase, ref, onValue } from "firebase/database";
import { db, firebase } from '../../firebase';
import 'firebase/compat/auth';
const HomeScreen = (props) => {
const [loadedData, setLoadedData] = useState([]);
const username = props.email.substring(0, props.email.indexOf('#'))
useEffect(() => {
readData();
}, [])
async function readData() {
db.collection('Users').get().then((querySnapshot) => {
querySnapshot.forEach(element => {
const incomingData = element.data();
setLoadedData(incomingData)
})
})
}
return (
<div className={styles['wrapper']}>
</div>
)
}
export default HomeScreen;

I created a simple project in react by using firebase to use crud proccessing.
https://github.com/celalaygar/web-push/tree/master/react-firebase-CRUD-example
in package.json->dependencies : "firebase": "^7.14.1"
in this file fetch data method is here.
https://github.com/celalaygar/web-push/blob/master/react-firebase-CRUD-example/src/components/main.component.js
fetchData = async () => {
const db = firebase.firestore();
const data = await db.collection("spell").get();
result = data.docs.map(doc => ({ ...doc.data(), id: doc.id }));
this.setState({ spell: result });
}

Related

log-in authentication with credentials, nextjs

I'm trying to add next-auth authentication with credentials(it's my first time and the source I'm using for following is using version 3 as I know, therefore there is a lot of difference and I couldn't find the right solution for days). Basically I have got form of registration and log-in form, and as a backend server I'm using mongodb. the registration form works normally, user is able to create account, but the log-in form doesn't seem to be working and in console when user sends request for logging in comes up error: {error: 'client.db.collection is not a function', status: 401, ok: false, url: null}.
This is pages/api/[...nextauth].js file.
import { verifyPassword } from "#/lib/auth";
import connectToDatabase from "#/lib/db";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
export default NextAuth({
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
async authorize(credentials, req) {
const client = await connectToDatabase();
const userCollection = client.db.collection("users");
const user = await userCollection.findOne({ email: credentials.email });
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = verifyPassword(credentials.password, user.password);
if (!isValid) {
client.close();
throw new Error("Could not log you in.");
}
client.close();
return { email: user.email };
},
}),
],
});
and this is loginForm.js that should send request to log-in.
import React, { useRef } from "react";
import Link from "next/link";
import { signIn } from "next-auth/react";
const LoginForm = () => {
const emailRef = useRef();
const passwordRef = useRef();
const submitHandler = async (e) => {
e.preventDefault();
const enteredEmail = emailRef.current.value;
const enteredPassword = passwordRef.current.value;
try {
const result = await signIn("credentials", {
redirect: false,
email: enteredEmail,
password: enteredPassword,
});
console.log(result);
} catch (error) {
console.log(error.error);
}
};
return (
<div
className="container d-flex justify-content-center align-items-center"
style={{ width: "100%", height: "100vh" }}
>
<div className="col-6-sm">
<form onSubmit={submitHandler}>
<div className="form-outline mb-4">
<input
type="email"
id="form2Example1"
className="form-control"
ref={emailRef}
/>
<label className="form-label" for="form2Example1">
Email address
</label>
</div>
<div className="form-outline mb-4">
<input
type="password"
id="form2Example2"
className="form-control"
ref={passwordRef}
/>
<label className="form-label" for="form2Example2">
Password
</label>
</div>
<button type="submit" className="btn btn-dark m-1">
Sign in
</button>
<Link href="/register" className="btn btn-dark m-1">
Register
</Link>
</form>
</div>
</div>
);
};
export default LoginForm;

Cannot get token

I wanna make a function that, When i submit the form i want to send the username and room name to the server to exchange for an access token, and if there's a token, my Room component will be rendered and you can see the roomName being rendered. However when i click the submit button, nothing happened. What's wrong with my codes?
VideoChat.js:
import React, { useState, useCallback } from "react";
import Lobby from "./Lobby";
import Room from "./Room"
function VideoChat() {
const [username, setUsername] = useState("");
const [roomName, setRoomName] = useState("");
const [token, setToken] = useState(null);
const handleUsernameChange = useCallback((event) => {
setUsername(event.target.value);
}, []);
const handleRoomNameChange = useCallback((event) => {
setRoomName(event.target.value);
}, []);
const handleSubmit = useCallback(
async event => {
event.preventDefault();
const data = await fetch("/video/token", {
method: "POST",
body: JSON.stringify({
identity: username,
room: roomName,
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => res.json());
setToken(data.token);
},
[username, roomName]
);
const handleLogout = useCallback((event) => {
setToken(null);
}, []);
Lobby.js:
import React from "react";
const Lobby = ({
username,
handleUsernameChange,
roomName,
handleRoomNameChange,
handleSubmit
}) => {
return (
<form onSubmit={handleSubmit}>
<h2> Enter a room </h2>{" "}
<div>
<label htmlFor="name"> Name: </label>{" "}
<input
type="text"
id="field"
value={username}
onChange={handleUsernameChange}
required
/>
</div>{" "}
<div>
<label htmlFor="room"> Room name: </label>{" "}
<input
type="text"
id="room"
value={roomName}
onChange={handleRoomNameChange}
required
/>
</div>{" "}
<button type="submit" > Submit </button>{" "}
</form>
);
};
export default Lobby;
return (
<div>
{token ? (<div><Room roomName={roomName} token={token} handleLogout={handleLogout} /></div>)
: (<div>
<Lobby
username={username}
roomName={roomName}
handleUsernameChange={handleUsernameChange}
handleRoomNameChange={handleRoomNameChange}
handleSubmit={handleSubmit}
/>
</div>)
}
</div>
);
}
export default VideoChat;
Room.js:
import React, { useState, useEffect } from "react";
import Video from "twilio-video";
const Room = ({ roomName, token, handleLogout }) => {
const [room, setRoom] = useState(null);
const [participants, setParticipants] = useState([]);
const remoteParticipants = participants.map(participant=> {
<p key={participant.sid}>{participant.identity}</p>
})
useEffect(() => {
const participantConnected = participant => {
setParticipants(prevParticipants => [...prevParticipants, participant]);
};
const participantDisconnected = participant => {
setParticipants(prevParticipants =>
prevParticipants.filter(p => p !== participant)
);
};
Video.connect(token, {
name: roomName
}).then(room => {
setRoom(room);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.participants.forEach(participantConnected);
});
});
return (
<div className="room">
<h2>Room: {roomName}</h2>
<button onClick={handleLogout}>Log out</button>
<div className="local-participant">
{room ? (<p key={room.localParticipant.sid}>{room.localParticipant.identity}</p>) : ''}
</div>
<h3>Remote Participants</h3>
<p>{remoteParticipants}</p>
</div>
)
};
export default Room;
Sandbox link: https://codesandbox.io/s/video-chat-y67nv?file=/src/Lobby.js

How do I retrieve an item data by id and pass it to another component in React16 with Hooks

I want to create an edit screen. I have a component called Task that looks like this
const Task = ({task}) => {
return (
<li>
<div>
<div>{task.text}</div>
{task.day}
</div>
<div className="icons">
<Link
to={`/edit/${task.id}`} >
<RiEdit2FillIcon />
</Link>
</div>
</li>
)
}
That goes to a parent component with a tasks.map() and then to the main component that will render the list of tasks. But from this component, I want to click on that Edit Icon and open an Edit screen that is already Routed like this <Route path='/edit/:id' component={EditTask}/> That EditTask component is what I am working on now
import React from 'react'
import {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import Task from './components/Task'
const EditTask = () => {
const api ="http://localhost:5000"
const [tasks, setTasks] = useState([])
const [task, setTask] = useState([])
const [text, setText] = useState('')
const [day, setDay] = useState('')
const [reminder, setReminder] = useState(false)
const onSubmit = (e) => {
e.preventDefault()
updateData()
}
//Get Request
useEffect(() => {
const getTask = async () => {
const tasksFromServer = await fetchTask()
setTasks(tasksFromServer)
}
getTask()
},[])
const fetchTask = async (id) => {
const res = await fetch(`${api}/tasks/${id}`)
const data = await res.json()
console.log(data)
return data
}
//Update request
const updateData = async (id) => {
const taskToEdit = await fetchTask(id)
const updateTask = {
...taskToEdit,
reminder: !taskToEdit.reminder,
text: taskToEdit.text,
day: taskToEdit.day
}
const res = await fetch(`${api}/tasks/${id}`, {
method: 'PUT',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(updateTask)
})
const data = await res.json()
setTasks(
tasks.map((task) =>
task.id === id
? {
...task,
reminder: data.reminder,
text: data.text,
day: data.day
}
: task
)
)
}
return (
<div>
<header className='header'>
<h1>Edit</h1>
<Link to="/" className="btn btn-primary">Go Back</Link>
</header>
<form className="add-form" onSubmit={onSubmit}>
<Task task={task}/>
<div className="form-control">
<label>Task</label>
<input type="text" placeholder="Add Task" value={text} onChange={(e)=> setText(e.target.value)} />
</div>
<div className="form-control">
<label>Day & Time</label>
<input type="text" placeholder="Add Day & Time" value={day} onChange={(e)=> setDay(e.target.value)}/>
</div>
<div className="form-control form-control-check">
<label>Set Reminder</label>
<input type="checkbox" checked={reminder} value={reminder} onChange={(e)=> setReminder(e.currentTarget.checked)}/>
</div>
<input className="btn btn-block" type="submit" value="Save Task" />
</form>
</div>
)
}
export default EditTask
I'm a bit lost here. I can't figure out how to pass the ID from Task.js to EditTask.js and populate the form with the data form that ID.
Thanks in advance
You can get id in EditTask with useParams in "react-router
import { useParams } from "react-router";
const EditTask = () => {
const { id } = useParams();
}

New to react...state not updating on login

Looking for a gentle push in the right direction. Working on a react project and using hooks. Yes, have read documents, but not fully understanding yes.
The ask is about a login routine. Login form works, but does not reflect failed login state until repeat submission; so I am getting previous state, not current.
Tried useEffect...no change. Code follows, and appreciated any constructive feedback:
From the Login form
import React, { useState, useEffect, useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Redirect } from 'react-router-dom'
import getAuthStatus from 'common/cyclone/auth/authenticated.status'
import {
authenticateByLogin,
authenticationSelector,
} from '../services/auth.service'
import Form from 'react-validation/build/form'
import Input from 'react-validation/build/input'
import CheckButton from 'react-validation/build/button'
const required = (value) => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
)
}
}
const Login = (props) => {
const form = useRef()
const checkBtn = useRef()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [errorMessage, setErrorMessage] = useState(null)
const dispatch = useDispatch()
const { session, hasErrors } = useSelector(authenticationSelector)
useEffect(() => {}, [session, hasErrors])
const onChangeUsername = (e) => {
const username = e.target.value
setUsername(username)
}
const onChangePassword = (e) => {
const password = e.target.value
setPassword(password)
}
const handleLogin = (e) => {
e.preventDefault()
setLoading(true)
form.current.validateAll()
if (checkBtn.current.context._errors.length === 0) {
dispatch(authenticateByLogin(username, password))
.then(() => {
setLoading(false)
if (hasErrors) {
setErrorMessage(session.error.message)
} else {
//props.history.push('/profile')
// window.location.reload()
}
})
.catch(() => {
setLoading(false)
})
} else {
setLoading(false)
}
}
if (session.success) {
//console.log(session.success)
return <Redirect to="/profile" />
}
if (getAuthStatus()) {
return <Redirect to="/profile" />
}
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form onSubmit={handleLogin} ref={form}>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
validations={[required]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
validations={[required]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block" disabled={loading}>
{loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{hasErrors && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{errorMessage}
</div>
</div>
)}
<CheckButton style={{ display: 'none' }} ref={checkBtn} />
</Form>
</div>
</div>
)
}
export default Login
From the auth slice:
/** Third Party Libraries */
import { createSlice } from '#reduxjs/toolkit'
import qs from 'qs'
/**Axios Wrapper...nothing fancy here*/
import CycloneAPIInstance from 'common/cyclone/api/api.client'
import CycloneConfig from 'config/base'
/** Main API Server URL */
const API_URL = CycloneConfig.API_URL
const session = JSON.parse(localStorage.getItem('authentication'))
/** Define Initial State */
export const initialState = session
? {
hasErrors: false,
session: session,
}
: {
hasErrors: false,
session: [],
}
/** Define Slice */
const authenticationSlice = createSlice({
name: 'authentication',
initialState,
reducers: {
authenticateUser: (state) => {
state.hasErrors = false
},
authenticateUserSuccess: (state, { payload }) => {
state.hasErrors = false
state.session = payload
console.log(state.session)
},
authenticateUserFailure: (state, { payload }) => {
state.hasErrors = true
state.session = payload
},
deauthenticateUser: (state) => {
state.session = []
},
},
})
export const {
authenticateUser,
authenticateUserSuccess,
authenticateUserFailure,
deauthenticateUser,
} = authenticationSlice.actions
export const authenticationSelector = (state) => state.authentication
export default authenticationSlice.reducer
export function authenticateByLogin(user_name, user_password) {
let requestBody = {
user_name: user_name,
user_password: user_password,
}
let config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
return async (dispatch) => {
dispatch(authenticateUser())
try {
const response = await CycloneAPIInstance.post(
API_URL + 'auth/login',
qs.stringify(requestBody),
config
)
//console.log(response.data.content)
localStorage.setItem('session', JSON.stringify(response.data.content))
dispatch(authenticateUserSuccess(response.data.content))
} catch (error) {
//console.log(JSON.stringify(error.response.data))
dispatch(authenticateUserFailure(error.response.data))
}
}
}
export function deauthenticateByLogout() {
return async (dispatch) => {
dispatch(deauthenticateUser())
localStorage.removeItem('session')
}
}
Try to set the message when hasError change
useEffect(()=> {
if(hasErrors) {
setErrorMessage(session.error.message)
}
}, [hasErrors]);
This is quite some code so I just skipped through to fix the problem and not pick everything apart. My best guess is this part:
dispatch(authenticateByLogin(username, password))
.then(() => {
setLoading(false)
if (hasErrors) {
setErrorMessage(session.error.message)
} else {
//props.history.push('/profile')
// window.location.reload()
}
})
.catch(() => {
setLoading(false)
})
Here you execute the async authentication and then do thing based on "hasError". This "hasError" comes from a hook. We (or at least I) have no clear idea how this is managed. Thing is you cant be 100% sure that hasError is really trustworthy at the point you check it in the then-block. The hook might run just after the next render, which explains why you see the previous state, not the actual one.
Best guess would be to use the response from that async call, because there should be one => authenticate.then((response) => if(response.hasError) ...)
With this check you can set your own error state and your component should be up-to-date
Let me know if this fixes your error.

Default value in input field lost after click

So, i'm making this social network app and it has user profile. if user wants to update profile, eg. name, by opening modal EditUser, the old value of users name should be there, in input filed, and user needs to have opportunity to change/update that.
I used 'defaultValue', and there is it, in input field, but if i don't change anything in that field, just click 'update', it will be lost. updated value is empty string then, and not the value that is showing in that field. how can i fix this?
Also interested how to set as default user image. so, user has profile image, and on update if user changes only name, not the picture or something else, everything else should be the same, but photo is also like input text field lost.
here is what i tried:
MyProfile.tsx
import React, { useState, useEffect, useContext } from 'react'
import './myprofile.css'
import Axios from 'axios'
import SinglePost from '../single_post/SinglePost'
import { AppContext } from '../context/AppContext'
import UpdateProfile from '../modals/UpdateProfile'
function MyProfile() {
const [userInfo, setUserInfo] = useState({
firstName: '',
lastName: '',
userBio: 'Write something about yourself.',
userPhoto: ''
})
const [isEditOpen, setIsEditOpen] = useState(false)
const { userID, setUserID } = useContext(AppContext)
// open modal on click 'edit'
const editUser = () => {
setIsEditOpen(true)
}
// get user data
const storedToken = localStorage.getItem('token')
useEffect(() => {
const config = {
headers: { "x-auth-token": `${storedToken}` }
}
Axios
.get('/api/auth/user', config)
.then(res => {
console.log('response', res)
const user = res.data.user
setUserID(user._id)
setUserInfo({
firstName: user.first_name,
lastName: user.last_name,
userBio: user.user_bio,
userPhoto: user.profile_image
})
})
.catch(err => console.log(err))
}, [])
return (
<div className="profile-container">
<button className="btn-edit" onClick={editUser}>
<i className="fa fa-edit"></i>
</button>
<div className="user-info">
<div className="img-circular">
<img className="user-profile-img2" src={userInfo.userPhoto}></img>
</div>
<p className="user-name">{userInfo.firstName} {userInfo.lastName}</p>
<p className="about-user">{userInfo.userBio}</p>
</div>
<div className="user-posts">
<p className="my-posts-title">My Posts</p>
</div>
{isEditOpen && <UpdateProfile
userID={userID}
setIsEditOpen={setIsEditOpen}
isEditOpen={isEditOpen}
setUserInfo={setUserInfo}
userInfo={userInfo}
/>}
</div>
)
}
export default MyProfile
UpdateProfile.tsx
import React, { useState, useRef, useEffect } from 'react'
import { Modal, ModalHeader, ModalBody, ModalFooter, Button, FormGroup, Label, Input } from 'reactstrap'
import Axios from 'axios'
import '../user_profile/myprofile.css'
function UpdateProfile(props: any) {
const [firstNameUpdated, setFirstNameUpdated] = useState('')
const [lastNameUpdated, setLastNameUpdated] = useState('')
const [userBioUpdated, setUserBioUpdated] = useState('')
const inputNameRef = useRef<HTMLInputElement | any>(null)
useEffect(() => {
console.log(inputNameRef.current, props.userInfo.firstName)
inputNameRef.current && (inputNameRef.current.value = props.userInfo.firstName)
}, [])
// upload image
const [file, setFile] = useState('')
const [uploaded, setUploaded] = useState('')
const handleImageUpload = (e: any) => {
e.preventDefault();
setFile(e.target.files[0])
};
const onClickHandler = (e: any) => {
const formData = new FormData()
formData.append('fileImage', file)
Axios.post("/api/image", formData, {})
.then(res => {
//console.log(`UPLOADED: http://localhost:5000/${res.data.fileImage}`)
setUploaded(`http://localhost:5000/${res.data.fileImage}`)
})
.catch(err => console.log(err))
}
// update user
const updateUser = (e: any) => {
e.preventDefault()
props.setIsEditOpen(false)
const formData = new FormData()
formData.append('fileImage', file)
formData.append('first_name', firstNameUpdated)
formData.append('last_name', lastNameUpdated)
formData.append('user_bio', userBioUpdated)
const config: any = { header: { "Content-Type": "multipart/form-data" } }
Axios
.put(`/api/users/${props.userID}`, formData, config)
.then(res => {
const user = res.data
props.setUserInfo({
firstName: user.first_name,
lastName: user.last_name,
userBio: user.user_bio,
userPhoto: user.profile_image
})
})
.catch(err => console.log(err))
}
return (
<div>
{props.isEditOpen &&
<Modal isOpen={props.isEditOpen} toggle={() => props.setIsEditOpen(!props.isEditOpen)} backdrop="static">
<ModalHeader>Update your profile</ModalHeader>
<ModalBody>
<FormGroup>
<Label>Profile Image</Label>
<Input type="file" name="fileImage" onChange={handleImageUpload}></Input>
</FormGroup>
<Button onClick={onClickHandler} className="btn-upload-img">Upload file</Button>
<div className="inline">
{uploaded ? <img src={uploaded} style={{ width: "100px" }}></img> : <img src={props.userInfo.userPhoto} style={{ width: "100px" }}></img>}
</div>
<FormGroup>
<Label>First Name</Label>
<Input type="text" onChange={(e: any) => setFirstNameUpdated(e.target.value)} defaultValue={props.userInfo.firstName}></Input>
</FormGroup>
<FormGroup>
<Label>Last Name</Label>
<input type="text" onChange={(e: any) => setLastNameUpdated(e.target.value)} defaultValue={props.userInfo.lastName} ></input>
</FormGroup>
<FormGroup>
<Label>About me</Label>
<Input type="text" onChange={(e: any) => setUserBioUpdated(e.target.value)} defaultValue={props.userInfo.userBio}></Input>
</FormGroup>
</ModalBody>
<ModalFooter>
<Button color="success" onClick={updateUser} className="btn-update">Update</Button>
<Button color="danger" onClick={() => props.setIsEditOpen(false)}>Cancel</Button>
</ModalFooter>
</Modal>}
</div>
)
}
export default UpdateProfile
I'm just trying to get the old value in input field (which i did), and user can choose if wants to change that or not. if not, old value should stay in updated profile, but in my case, on click 'update' it is lost in user profile.
In UpdateProfile, you should initialise the states with the value you got in props and later they could change.
const [firstNameUpdated, setFirstNameUpdated] = useState(props.userInfo.firstName)
const [lastNameUpdated, setLastNameUpdated] = useState(props.userInfo.lastName)
const [userBioUpdated, setUserBioUpdated] = useState(props.userInfo.userBio)
Initialising the state could solve all your problem.

Resources