Unwanted delay displaying user name and profile picture in React - reactjs

I have a web page/app written in React that has two components, main page and a navigation bar at the top of the page. The navigation bar has a Google login button component that allows the user to log in with their Google account. When the user logs in, the navigation bar also has a component that shows the user name and profile picture. Here is an example screenshot:
Imgur
The problem is that when the user navigates to other pages, the avatar and text always take about a second to show up, making the page navigation janky.
When the user navigates to other page, the user avatar and name take a second to show up. This causes everything else on the page to suddenly move downwards.
Here's some parts of the code:
App.js
const App = () => {
return(
<div>
<Navigation/>
<Routes>
<Route path="/" element={<Landing/>} />
<Route path="/extrainfo" element={<ExtraInfo/>} />
<Route path="/test" element={<Test/>}/>
<Route path="/userpage" element={<Userpage/>}/>
</Routes>
</div>
);
}
Navigation.js
const Navigation = () => {
let userInfo = JSON.parse( localStorage.getItem('userInfo') );
const [isLoggedIn, setIsLoggedIn] = useState(false);
const navigate = useNavigate();
const onSuccess = async (googleRes) => {
localStorage.setItem("user", JSON.stringify(googleRes));
setIsLoggedIn(true);
try{
let userResponse = await UserService.getUser()
if(userResponse.status === 200){
setIsLoggedIn(true);
navigate("/userpage");
}
}
catch(error){
if (error.response.status === 404){
/* navigate("/extrainfo"); */
}
const resMessage = (error.response && error.response.data && error.response.data.message) || error.message || error.toString();
console.log(resMessage);
}
};
const onFailure = (err) => {
console.log('failed', err);
};
const onLogout = (res) => {
console.log("log out");
UserService.logout();
setIsLoggedIn(false);
}
useEffect(() => {
document.title = 'Navigation';
const start = () =>{
gapi.client.init({
clientId: process.env.REACT_APP_GOOGLE_CLIENT_ID,
scope: 'email',
});
}
gapi.load('client:auth2', start);
}, []);
return (
<nav className="dt w-100 border-box pa2 ph5-ns bg-#f5f5f5">
<a href="/" title="Home">
<img src="mylogo.png"
className="dib w3 h3 br-100" alt="Site Name"/>
</a>
<div className="dtc v-mid w-40 tr">
{ isLoggedIn ? (
<GoogleLogout
clientId= {process.env.REACT_APP_GOOGLE_CLIENT_ID}
buttonText="Logout"
onLogoutSuccess={onLogout}
/>) : (
<GoogleLogin
clientId= {process.env.REACT_APP_GOOGLE_CLIENT_ID}
buttonText="Sign in"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
isSignedIn={true}
/>
)}
</div>
<div className="dtc v-mid w-10 tr">
<a className="link dim white f6 f5-ns dib mr3 mr4-ns bg-dark-gray pa2" href="landing" title="Landing">Enter page</a>
</div>
<MyAvatar isLoggedIn={isLoggedIn}/>
</nav>
);
}
MyAvatar.js
const MyAvatar = (isLoggedIn) => {
const usrData = JSON.parse( localStorage.getItem('user') );
let usrObj = {};
let usrName = null;
let usrImg = null;
if (usrData) {
usrObj = usrData['profileObj'];
usrName = usrObj['name'];
usrImg = usrObj['imageUrl'];
}
if(isLoggedIn.isLoggedIn === true ){
return(
<div className="dtc v-mid w-10 pa1 tc">
<img
src={usrImg}
className="br-100 h3 w3 dib" alt={usrName + ' avatar'}>
</img>
<p className="avatar_name">{usrName}</p>
</div>
)
}
}

it seems like the information tooks a bit longer then the rest to load. Usually if you would use a styling library, you could use skeletons. These elements are like placeholders which are shown until the data is loaded properly. You see them for example in many social media apps, when new content is loading but not displayed yet. But this is a intermediate thing to learn.
What could be more helpful in your case, is to move the loading part of your app from the navigation component into the main App. By this, you prevent a new loading everytime the Navigation element is rendered, which I assume causes the delay. Or, even better would be a function which is only called when someone clicks the login button.
You should have a look at stores also. I assume you're learning the basics, so stores will cross your ways sooner or later, but for your case, they are pretty good, since you can access them from everywhere and don't have to deal with propdrilling.
Just put the Userdata in a store and acess the store in the Navigation element. That should do the trick. Have a nice day and keep on learning.

Related

I want to redirect to another page which consist only the details of the individual object on which the user clicks. Someone help me how can i do this

I want to redirect to another page which consist only the details of the individual object on which the user clicks. Someone help me how can i do this. Thanks in advance.
function Dashboard() {
const [token, setToken] = useContext(store)
const [events, setEvents] = useState([])
useEffect(()=>{
axios.get('http://localhost:5000/api/events').then(res => {
console.log(res)
setEvents(res.data)
}).catch(err => {console.log(err)})
}, [])
if(!token){
return (
<Navigate to="/login" replace={true} />
)
}
//using map function to show data in a list. Here i want to access single item when i click on it//
return (
<>
<div className='eventelement'>
<ul>
{
events.map(event => (
<section className=''>
<li key={event._id}>
<table className='eventtable' border={1}>
<th className='eventth'><div className='eventtitle'> {event.eventname} </div></th>
<tr><div className='eventhoster'> hosted by: {event._id} </div></tr>
<tr><div className='eventdetails'> <label className='eventsh'>Event type: </label>{event.eventtype} </div></tr>
</table>
</li><br />
</section>
))
}
</ul>
</div>
<div>
</div>
</>
)
}
export default Dashboard
Give each element a click event handler that redirects the user to, say, /events/${event._id}. If you're using a recent version of react-router-dom, its useNavigate hook should give you a callback to navigate to another page.
Then add a <Route path="/events/:id" element={<EventDetailPage/>} /> to your routes.

react-toastify popup showing 2 times

Website error visual on chrome
I create a react website. On this website, I create a social login icon using firebase-hooks. But when I click on the social login button the pop-up shows. But after closing that pop-up I use react toastify to show the error. But always It's showing twice. and can't fix this problem
const SocialLogin = () => {
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || "/";
const [signInWithFacebook, facebookUser, facebookLoading, facebookError] =
useSignInWithFacebook(auth);
facebookError && toast.error(facebookError.message);
const [token] = useToken(facebookUser);
token && navigate(from, { replace: true });
return (
<div>
<div className="or">
<div></div>
OR
<div></div>
</div>
<div className="social-logins">
<p>{facebookLoading && `Loadin Please Wait`}</p>
<div className="social-btn" onClick={() => signInWithFacebook()}>
<SiFacebook />
<span>Facebook</span>
</div>
</div>
<ToastContainer pauseOnHover />
</div>
);
};
toast message will be appeared on every re render , you need to call toast when you get error message from fb , you need to call toast inside of useEffect, something like this
React.useEffect(() => {
if (facebookError.message) {
toast.error(facebookError.message);
}
}, [facebookError.message])

How to render Video Page when the button is clicked before taking the next action in React.js

how can I show a video when the button is clicked and then it performs an
action , so step by step:
user lands to the main page
user clicks on the button that should take him/her to home page
I used useNavigate to change the route of the page to /home, but before doing that, I want the user to see short video.
so far this is what I came up with, but it doesnt work
const Main = (props) => {
let navigate = useNavigate();
const playVideo = () => {
return (
<div>
<video src={video}></video>
</div>
)
}
const handleClickHome = async (event) => {
event.preventDefault();
await playVideo()
return (
navigate("/home")
)
}
return (
<div className={classes.main} >
<button onClick={handleClickHome} className={classes.button}> <h2> LET ME IN </h2> </button>
</div >
);
}
export default Main ```
You can show your video by clicking the button and use under below event for navigation.
document.getElementById('videoId').addEventListener('ended',myHandler,false);
function myHandler(e) {
// do navigation
}

Redirect to another page in reactJS onClick

I'm working on a reactJS web App , im stuck in this problem . I want to use or any other method to redirect to another page after testing a value from the server.
I tried this :
<Link to='/Upload'>
<Button
variant="contained"
color="primary"
onClick={async () => {
await verificationp(phone, code).then(async (result) => {
if ((await result) === true) {
//I want to redirect to another page
} else {
//I want to display an alert
}
});
}}
>
Submit
</Button>
</Link>
You don't need to use the Link component in this example. Try using the history prop (props.history.push('/')) See the below examples, the last one should apply to you:
In your App.js router:
<Switch>
<Route path="/auth" component={Auth} />
<Route path="/errorpage" component={ErrorPage} />
<Route path="/" exact component={HomeScreen} />
<Redirect to='/' />
</Switch>
On the page where you want to redirect:
import React, { useState } from 'react'
const Auth = props => {
const [form, setForm] = useState({
data: {
email: '',
password: ''
}
})
const authHandler = async (event) => {
event.preventDefault()
try {
// await dispatch(action) // dispatch to redux or send a fetch
props.history.push('/') // redirects if no errors
} catch (err) {
props.history.push('/errorpage') // redirects if an error
}
}
const inputChangedHandler = e => {
setForm({
...form,
data: {
...form.data,
[e.target.id]: e.target.value
}
})
}
return (
<div>
<form onSubmit={authHandler}>
<input id='email' type='email' value={form.data.email} onChange={inputChangedHandler} />
<input id='password' type='password' value={form.data.password} onChange={inputChangedHandler} />
<button type='submit'>Login</button>
</form>
</div>
)
}
export default Auth
In your case try the below if the page is on the route stack the history prop will be available or you will have to use withRouter or useHistory hook to get access to the history prop.
const clickHandler = () => {
try {
// await dispatch(action)
props.history.push('/') // redirects
} catch (err) {
props.history.push('/errorpage') // redirects
}
}
return (<button type='button' onClick={clickHandler}>Click Me</button)
Here is the Redirect doc
I think best thing you can do is create a separate function for onClick and change a state to redirect, so this way you can prevent some action by user while request is in progress
const [redirectTo, setRedirectTo] = useState('');
const chekcfromServer = async () => {
await verificationp(phone, code).then(async (result) => {
if ((await result) === true) {
setRedirectTo('/somewhereinyour/application')
} else {
// I want to display an alert
}
});
};
and when in render
return (
<>
redirectTo && <Redirect to={{pathname: redirectTo }} />
...rest of your code
</>
)
You can use "redirect" from "react-router-dom"
Example:
import { Redirect } from "react-router-dom";
<Link to='/Upload'>
<Button
variant="contained"
color="primary"
onClick={async () => {
await verificationp(phone, code).then(async (result) => {
if ((await result) === true) {
//I want to redirect to another page
return <Redirect to="your_url" />
} else {
//I want to display an alert
}
});
}}
>
Submit
</Button>
</Link>
I tend to use the state system (Redux) to resolve this issue.
So my problem was I wanted to fire an event.preventDefault() to stop a link being clicked, but then I wanted to do an API call after it (to check login status, as it was a complex IoT device) and click the link if the check was fine, but not click the link if it wasn't. However, the event.PreventDefault() stopped me doing this, because it cancels any events (I'm sure there are nicer ways to achieve this! but time targets and all that).
I solved this by:
Having my usual async function with the event.preventDefault() inside it.
Within the first call I set some state in Redux, just a basic boolean.
If the API check was successful then I call the same click function again (using .click()) and bypass the event.preventDefault() thanks to Redux (then reset the state afterwards) which forces the re-direct. If the check failed then I display an error message and don't display again (while obviously updating Redux).
The State Management system inside React is very powerful. You can do some hacky things! but sometimes the other "correct" solutions don't work for you; for example the history props functionality, which you can get round that other ways via State Management.

React - Browser's Back button doesn't work

In the following component, if I click on any of the two buttons, the URL in the address-bar gets changed.
In the list view if you click on the button Details the page get rendered and shows that particular item and the URL in the address bar get changed too.
And in the user view if you click on the "Back to overview" button, the page renders back to the list view and the URL gets changed again.
import React, { useState, useEffect, Fragment } from 'react'
import axios from 'axios'
const UserList = ({ id, setID }) => {
const [resources, setResources] = useState([])
const fetchResource = async () => {
const response = await axios.get(
'https://api.randomuser.me'
)
setResources(response.data.results)
}
useEffect(() => {
fetchResource()
}, [])
const renderItem = (item, userId) => {
const setURL = (e) => {
window.history.pushState(null, null, '/' + e)
setID(item.login.uuid)
}
const clearURL = (e) => {
window.history.back()
setID(null)
}
return (
<Fragment key={item.login.uuid}>
{userId ? (
// User view
<div>
<img src={item.picture.large} />
<h2>
{item.name.first}
</h2>
<p>
{item.phone}
<br />
{item.email}
</p>
<button onClick={() => clearURL('/')}>
Back to overview
</button>
</div>
) : (
// List view
<li>
<img src={item.picture.large} />
<h2>
{item.name.first}
</h2>
<button onClick={() => setURL(item.login.uuid)}>
Details
</button>
</li>
)}
</Fragment>
)
}
const user = resources.find(user => user.login.uuid === id)
if (user) {
// User view
return <div>{renderItem(user, true)}</div>
} else {
// List view
return (
<>
<ul>
{resources.map(user => renderItem(user, false))}
</ul>
</>
)
}
}
export default UserList
Everything is working fine.
However, the problem with this solution is that on user view, I cannot use the browsers back button to go back to the list view page.
Is there any way I can change this without using React Route?
So what I believe is happening is you are over-writing the 'history' of the browser. The 'history' is pretty much just a stack of paths you've been and when you click the back button it just pops the current path off the stack. I think that when your using 'window.history.pushState(null, null, '/' + e)' its setting the history = to null so there is nothing to go back to. I would recommend using react-router and just pushing a path ie. router.push('/whatever'). There are a bunch of ways to interact with the history through react without using the native history object. If you want to use the native history object https://developer.mozilla.org/en-US/docs/Web/API/History_API
Edit - I apologize your not overwritting the history, but I do still believe that the error is coming from passing a 'null' value as the 'state' param for the pushState method

Resources