Managing exit of a user from a game 'room' on React? - reactjs

This might seem complicated - I am working on a game app on React that involves users all joining a certain <Route> in my project. When all users are on this route, the game is unpaused and if not the game is paused.
My question is: How can I manage to suddenly pause the game if a user leaves the link or closes the tab ? Is there any way to actively check this on react? I figured useEffect hook might be useful but I don't know how to get the number of users leaving a site or closing the tab in real-time at once.
Here is some basic code to understand what I want. I have a functional React component <GameSession> that hosts a game when users join this link:
const GameSession = ({userRegistered}) =>{
const [paused, setPaused] = useState(false);
useEffect(() => {
// store some user related data in localStorage when the user loads
}, []);
useEffect(() => {
setPaused(numberofUsersInGame === totalUsers ? false : true)
}, [numberofUsersInGame]);
/* MY PROBLEM IS IN THIS EFFECT HOOK ABOVE. HOW TO CHECK IF A USER HAS LEFT THIS
COMPONENT OR PAGE SO I CAN UPDATE numberofUsersInGame ? */
if(!userRegistered)
return <Redirect to="/dashboardpage">
if(paused)
return <PausedComponent /> /*this component should fire a return when total
number of users registered to this game is not
equal to current number of users who are
active on the GameSession component (this link)*/
return (
<Fragment>
<GameComponent>
</Fragment>
);
};
GameSession.propTypes = {
// PropTypes go here
};
const mapStateToProps = (state) => ({
//states go here
});
export default connect(mapStateToProps, mapDispatchToProps)(GameSession);

Related

Do I need to store isUserSignIn for componet rendering in Local Storage(React,Next,Firebase Auth)

(Firebase Google Signin + React(Next) )
If user sign in , setState to true.
And render the home component and welcome(login) component in index.js by state value.I made an application by storing that state in local storage and I think it is not a good practice. So if I don't use local storage there is a issue when user signed in and it should reach the home component but , user can also see welcome component before home component.
Logic index.js -> if userSignin (Home) : (Login)
Conditional Rendering
{isSignin ? (<>Home Component</>) : (<Welcome Component/>)}
state
const [isSignin, setisSignin] = useState(false);
changing state if user signin
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
setisSignin(true);
console.log(user);
}else {
setisSignin(false);
}
});
}, []);
firebase auto use localstorage when u signin an account. please check it in your browser localstorage. You don't need to store it by yourself. Then, when user revisit that website from the same web browser, firebase auto login (unless he did not logout). However, that auto login needs some amount of time. you need to do something during that amount of time. after that time, isSignIn will be true. I suggest something like this.
const [isSignIn, setSignIn] = useState(null);
useEffect(()=> listener_to_authstate_change() ,[])
if (isSignIn === null) return null; // something you like to show example - <Loading />
if (isSignIn === false) return <SignIn Component />
if (isSignIn === true) return <Home Component />

React Firebase UseEffect Firestore

I've got a user bar component directly adjacent to my navbar, showing avatar and display name (user details saved within a userid-named doc). I followed the firestore documentation for real time updates. Placed inside a use effect hook, which has a dependency array, but is firing repetitively, firing off about 4,000 calls a minute per logged in user).
I really just want the snapshot to update when a user updates their display name or avatar. (found on a different form component)
I console logged every useEffect hook in my project to narrow it down to this component. Is there something about the dependency array causing this? Is there a better way to write this? Thanks!
export default function UserBar() {
const { user } = useAuthContext()
const [me, setMe] = useState([])
useEffect(() => {
//user real-time subscription for avatar and displayname
const auth = getAuth()
const fireUser = auth.currentUser
const unsub = onSnapshot(doc(db, "users", fireUser.uid), (doc) => {
if (doc.exists()) {
setMe(doc.data())
console.log('looking for duplicate - userbar unsub')
} else {
console.log("No such document!")
}
})
return () => unsub()
}, [me])
return (
<>
{user && (
<div className="user-bar">
<p className="oneHunid">Welcome
<span className="bold"> {user.displayName}!</span>
</p>
<Avatar src={me.photoURL} />
</div>
)}
</>
)
}
You do not want to have [me] as a dependency when inside the useEffect() you call setMe(). That leads to an infinite loop:
me gets a new value, so render
me has a new value, so run useEffect()
useEffect() calls setMe()
go to # 1
Based on the above, it looks like what you really want is to have your useEffect() be dependent on the value of auth.currentUser. So get that value outside of the useEffect() and then check to be sure it is not null. If it is not null, then start the onSnapshot() listener.
BTW: if it would be of help, check out the user profiles with Firebase Auth project I developed. In particular, check out the FirebaseProvider.js which is where I configure the Firebase project and get all of its services, and AuthProvider.js which provides login, logout, registration and "user profile".

Fetch data after login

I have a login component:
const login = async (e) => {
e.preventDefault();
const {data} = await loginUserRes({variables: {
...formData,
}});
if (data) {
currentUserVar({
...data.loginUser,
isLoggedIn: true,
});
}
};
Which allows user to send some data to the server, if the credentials are correct a JWT is stored in a cookie for authentication.
I'm wondering what he best approach is to fetching data after a log in, on the dashboard I have a list of movies that the user has added. Should I use a react effect to react to the currentUserVar changing and then do request for the movies? Or perhaps I could add all the dashboard data to the return object of the apollo server, that way the data is present on login.
Yes you should react to your authorization data, But this info always considered as global state that you will need it in the whole app. so It is recommended to save your currentUserVar in a react context or whatever you are using to handle global state, Even if the app was small and you don't have global state then you can left it to your root component so you can reach this state in all your components.
Here how I handle auth in reactjs, I don't know if it is the better approach but I'm sure it is a very good solution:
Create a wrapper component named AuthRoute that checks if the user is logedin or
not.
Wrap all components that require user auth with AuthRoute
If we have user data then render wrapped component.
If there is no user data then redirect to Login component
const AuthRoute = ({component, children, ...rest}) => {
const Component = component;
const {globalState} = useContext(GlobalContext);
// Return Component if user is authed, else redirect it to login page
return (
<Route
{...rest}
render={() =>
!!globalState.currentUser ? ( // if current user is exsits then its login user
children
) : (
<Redirect
to={ROUTES_PATH.SIGN_IN.index}
/>
)
}
/>
);
};
Then any component can fetch the needed data in it's mount effect useEffect(() => { fetchData() }, []). and this is an indirect react to userData.
Note: if you are not using routing in your app, you still can apply this solution by converting AuathRoute component to HOC.

How to to be displayed reactour just first time in nextjs?

I'm using reactour in next.js
I want to just first time when page is rendered reactor to be displayed, and when route change and come back to this page reactor not to be displayed
how can do this?
this is how I call it
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import dynamic from "next/dynamic";
const Tour = dynamic(() => import("reactour"), { ssr: false });
const tourConfig = [
{
selector: ".step_1",
content:
'Click "View future forecast earning" to look at all....',
},
{
selector: ".step_2",
content:
"Chose different earning forecasts to see how your property...",
},
];
export default function MyPage(props) {
const disableBody = target => disableBodyScroll(target);
const enableBody = target => enableBodyScroll(target);
const [isTourOpen, setIsTourOpen] = useState(false);
const closeTour = () => setIsTourOpen(false);
useEffect(() => {
const timing = setTimeout(() => {
setIsTourOpen(true);
}, 2000);
return () => {
clearTimeout(timing);
};
}, []);
return (
<>
<OtherComponent />
<Tour
onRequestClose={closeTour}
steps={tourConfig}
isOpen={isTourOpen}
rounded={5}
onAfterOpen={disableBody}
onBeforeClose={enableBody}
className={classes.root}
disableInteraction
inViewThreshold={50}
/>
</>
);
}
This entirely depends on your logic you can do this in 3 ways if you like:
The local storage way:
localStorage.setItem('displayTour', true);
If you're not clearing out the local storage then you can always check your storage whenever the user signs in to check (in useEffect) if the tour is displayed to them or not.
Storing in cookies:
You can always set cookies for the first time the user signs in and check, but this approach has a con, cookies are set in the browser so if the user has already gone through the tour and they sign in from another browser the tour will be rendered again.
Tour Flag from API (Network Request):
This may be the right thing to do if you are not trying to save your network requests. You can always send back a flag from backend e.g is_tour_displyed: false and can always validate whether to show the tour or not.
Note: The localStorage and the cookies may be cleared out anytime, considering if that's not a problem for you. You can always go for the third option.
you can make condition and add a value to localStorage like blow:
localStorage.setItem('playTour', 'true');
and when ever you played the tour at first time you can set this value to false

React component state persist from firebase

I have a simple shopping site, with the data coming from a headless CMS system. I am using nextJS/React to build out the site. When a user adds items to the cart, I want to use firebase to persist the state, as the user navigates different pages. However, I lose all data in the state as well as firebase, when the user navigates to different pages. The main problem is I can't set the initial cart state to the firebase data. How do I solve this problem, so i can use the firebase data to persist cart state?
import React, { useState, useEffect } from 'react';
import Layout from './../components/Layout';
import contextCart from './../components/contextCart';
import firebase from '../data/firestore';
export default function MyApp({ Component, pageProps }) {
//any way to set the initial cart state to data from firebase???
const [ cart, setCart ] = useState([]);
const addToCart = (product) => {
setCart((prevCartState) => {
return [ ...prevCartState, product ];
});
firebase.database().ref().set(cart);
};
//componentDidUpdate => listen for cart changes and update the localStorage
useEffect(
() => {
firebase.database().ref().set(cart);
},
[ cart ]
);
//componentDidMount => run the effect only once and run cleanup code on unmount
useEffect(() => {
//effect code
firebase.database().ref().once('value').then(function(snapshot) {
setCart(snapshot.val() || []);
});
//returned function will be called on component unmount aka cleanup code
//return () => { /*any cleanup code*/ };
}, []);
return (
<contextCart.Provider value={{ cart: cart, addToCart: addToCart }}>
<Layout>
<Component {...pageProps} />
</Layout>
</contextCart.Provider>
);
}
The data is not updating because you are not listening to the changes in your code.
You specify to get the data once in the useEffect Hook , which only runs on the initial load.
To listen to changes you can implement it like this:
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', function(snapshot) {
updateStarCount(postElement, snapshot.val());
});
read more about it in the official firebase docs here: https://firebase.google.com/docs/database/web/read-and-write
I would recommend you to use a state-management library like Redux or even the Context API from React itself.
The reason for this is that firebase charge you for the traffic to your database (this is a bigger issue for the firestore db than the real-time db but still there are costs to consider).
With a state library, you can check if you need to fetch the data from firebase or if you already have what you need in your state.
Also if you can split your code up with the Provider Pattern, this makes it easier to maintain the logic while your application is growing.

Resources