Display different navbar for logged user - javascript

I've created login/register in my app, and now i want to display different navbar based if user is logged.
I don't use redux for this project.
In App component, globally, i created state that i wanted to pass to childs, and then base on it, display different data, so i created something like this:
function App() {
const [loginStatus, setLoginStatus] = useState();
useEffect(() => {
if(!loginStatus) {
axios.get("http://localhost:5000/api/users/isUserAuth", {
headers: {
"x-access-token": localStorage.getItem("token")
}
}).then((response) => {
console.log(response);
setLoginStatus(true);
})
}}, [])
and then in my Navbar i created something like this:
<NavItem style={{display: loginStatus ? "block" : "none"}}>
This works, but it causes loginStatus to be loaded every page refresh, every route made, so when i go to different page when user is logged, it causes 0.5s delay before NavItem will appear, because it makes request to backend on every refresh.
How can i store information that user is logged in so it wouldn't have to make API calls on every page?

I think you can use localStorage to store user logged-in information.

You need to initialize state with the local storage token and check with that state
const [loginStatus, setLoginStatus] = useState(token)

Related

Access route params in a single way react native

I am currently facing a problem with the authentication flow and the navigation file. Basicly I want to have the authentication logic inside my navigation file, so I can conditionaly say if I want to show the login or not.
Like this:
const { id: contextId } = useId()
const { token } = useToken()
const _id = contextId : getIdParam(contextId)
const globalParams = { initialParams: { _id } }
And then I use the _id to conditionally show or not the login. The problem with this implementation is, that I am using two ways to access the id param in the url:
ContextAPI (I set the id after login so it is available in that session)
getIdParam function, to access the id param via location.path, whenI try to reload a screen without passing in login.
Is there any way I can access params in the navigation file, I tried to use useRoute hook, but I am not allowed to use it outside of a screen.
Any help? Thanks

Data from firestore it's being fetched multiple times when login and logout (vanilla JS)

Well I made this Library app, where an user can login and add books. So, when a user login the app fetch data from a firestore collection, cool. The problem exists when the user login once, logout and then login again without refreshing the app. If the user do this twice, the fetch twice, if thrice, the fetch thrice. The code that executes multiple times its the fetchBooks(), the signInWithGoogle() only executes once. Here's the code involved:
function signInWithGoogle(){
const provider = new firebase.auth.GoogleAuthProvider()
auth.signInWithPopup(provider)
.then(result => {
// Create the new user document in firestore
createNewUserDocument(result.user)
// fetch feed data
auth.onAuthStateChanged(user =>{
user? fetchBooks() : null
})
}).catch(err => {
console.log(err)
})
signUpForm.reset()
signUpModal.hide()
signInForm.reset()
signInModal.hide()
}
function fetchBooks() {
const docRef = db.collection('users').doc(auth.currentUser.uid).collection('books')
docRef.get().then(querySnapshot =>{
console.log(querySnapshot)
querySnapshot.forEach(doc => {
const data = doc.data()
console.log(doc.data());
addCardToHTML(data.title, data.author, data.pages, data.description, data.read)
})
})
}
onAuthStateChanged is a subscription that triggers itself when there's a change in the user's authentication state.
So it will trigger when you log in, when you log out, etc.
So ideally you'd want to wait until the user logs in, and then call the fetchBooks() function, but if you keep doing it inside of the subscriber the function will trigger any time the subscriber emits a new value.
I would recommend starting with a restructure of your code to have functions that do individual things. Right now, you have a function signInWithGoogle. That function should only sign the user in with Google and return a promise with the result of that sign in. Instead, you have it signing in the user, fetching books (which itself is also fetching books AND modifying the DOM), and calling methods on your signUp elements.
Restructuring this to have some other top-level function would likely help you handle your problem easier. Specifically, try something like this:
function handleSignIn() {
signInWithGoogle()
.then(fetchBooks)
.then(books => {
books.forEach(book => addCardToHTML(...))
})
}
This is a good start because now it's clear what each individual function is doing. So now to handle your specific issue, I'll assume that the problem you're facing is that you're seeing the books be added multiple times. In that case, I would think what you'd want to happen is that:
When a user is signed in, you want to load their books and display them on the page.
When they log out, you want the books to be unloaded from the screen
When they log back in, the books are re-loaded and displayed.
If all of those assumptions are correct, then your problem wouldn't be with the code you have, but rather the signout functionality. When the user signs out, you need to add a function that will remove the books from the HTML. That way, when they sign back in after signing out, the handleSignIn function will kick off again and the addCardToHTML function will be running on a blank HTML page rather than a page that already has the cards.
Example:
function handleSignOut() {
signOut()
.then(clearBookCards)
}
function clearBookCards() {
// Manipulate DOM to remove all of the card HTML nodes
}

How to deal with situations where loading is interrupted in react-redux applications

I have the following example to illustrate how I update the redux state while loading data from the database in a react-redux application. learningActionDataOperationBegin sets loading (in the reducer) to true, then once the data is fetched from the database, fetchLearningActionsSuccess sets loading back to false. Inside the component, where the database records are displayed, FetchLearningActions is called only when initially loading is false. I believe this is the common practice with react-redux applications.
However, for example if the browser is closed or if the internet gets disconnected while the data is being loaded, the loading stays as true, which is a problem since in the next visit to the page FetchLearningActions will not get called (because loading is still true) and the page will always display Loading.. text on the screen. I wonder how I should handle this. Any ideas or suggestions?
export function FetchLearningActions(submissionId) {
var url = 'api/LearningAction/FetchLearningActions';
return dispatch => {
dispatch(learningActionDataOperationBegin());
axios.get(url, { params: { submissionId } })
.then(response => {
console.log('Learning actions are loaded.');
const learningActions = new schema.Entity('learningActions');
const normalizedData = normalize(response.data, [learningActions]);
dispatch(fetchLearningActionsSuccess(normalizedData.entities.learningActions))
})
.catch(error => { learningActionDataOperationFailure(error) });
}
}
You need to wrap learningActionDataOperationFailure(error) with dispatch. That action should set isLoading to false and maybe set an error flag. You now can differentiate between states where your app is making a request and whether it finished successfully or not.

Redux do not update React form inputs

I'm building a user control panel and I have issues binding current data to the input fields.
On page load all inputs are empty, as soon as I update the code, after the hot reload, the inputs are populated correctly...
Basically, in order to populate the form I need to fetch for the current authenticated user, then set the profile data:
componentDidMount() {
const {
getCurrentUser,
setProfileData,
setContactsdata,
setFaresData,
authenticated,
} = this.props;
if (authenticated) {
getCurrentUser(() => {
const {
user,
} = this.props;
if (user.profile) setProfileData(user.profile);
if (user.contacts) setContactsdata(user.contacts);
if (user.fares) setFaresData(user.fares);
});
}
}
I can not directly bind user data because the form has several widget where user can create or update only their profile, contacts, fares, etc... So all of those have their own Redux crud methods.
Everything work as expected, I just need to understand why my data is not visible on page refresh... and yes only after a code reloading...
I can confirm the reducers return the correct data.

Send Info after browserHistory.goBack

I'm building an App with reactjs and in some point I have a SearchForm, then I click on one of the items and in that view I have a back button.
What I want is to populate the search form with the previous data. I already achieved it with localStorage from javascript. And saving the state on componentWillUnmount, the problem is this data is always loaded even if I don't arrive from the go back.
Is there a way to add a kind of state to the goBack to tell it?
So, the best way here is to not use localStorage, and instead use history state.
When the user updates the form, you can do this (optionally debounce it for better performance):
history.replaceState(this.props.location.pathname, {
formData: JSON.stringify(formData),
});
Then when the component loads, you can access that state like this:
const {location} = this.props;
const routeState = location.state && location.state.formData;
if (routeState) {
this.setState({
data: JSON.parse(routeState),
});
}
If they refresh the page, or go to another page and go back, or go back and then go forward, they'll have that state. If they open it in a new tab, they won't have the state. This is usually the flow you want.

Categories

Resources