Redux do not update React form inputs - javascript

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.

Related

Dispatch action in useEffect's cleanup function

I have a form component in a Material UI <Tab /> which allows users to update their address info. The information is fetched from the API via redux-thunk and the form fields are filled with data from the server before the update has happened.
The problem is that after the user fills the fields with new data and updates their address, the form fields still show the previous state and don't update until the user reloads the page.
To fix the problem, I have implemented useEffect to dispatch the action of fetching user data whenever the isUpdated state is true (which will happen after the updateUserAddress action is fulfilled). This, of course, introduced another issue which was infinite rendering.
In order to fix the infinite dispatch call, I dispatch an action to clear the update state and set it to false in the useEffect's cleanup function. This has solved the issue and whenever the user updates their address, the UI updates and the new values populate the form fields:
// Refresh user after address update
useEffect(() => {
if (updateState) {
dispatch(getUser(userInfo.user_id));
}
// Clear update state
return () => dispatch(clearUpdateState());
}, [dispatch, updateState, userInfo]);
The only problem is that the cleanup function is called every time the user changes back and forth from the Edit Address tab.
Although this approach has fixed my problem, I was skeptical about it and wanted to know if this is the right way of doing this.
The problem is that after the user fills the fields with new data and updates their address, the form fields still show the previous state and don't update until the user reloads the page.
You can do an optimistic update when the update address action is dispatched and simply write the input data into the state - assuming that the update will probably work. This should fill the inputs with the data the user just entered, so for them nothing will change. The update request itself should return the updated data set, so you can save that in redux. Or if it doesn't, fire a get request right after the update.

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 render input fields based on selection after a DB query (Reactjs)?

What I'm trying to do here is load the page with an input that can be selected or unselected and the value will be predefined from a database check. So I visit the database before the render is done and I return a true/false but whenever the true is selected I can see that the page loads before applying the changes I required. So if a button was supposed to be disabled then it would first load as it is and then the disabling would follow. So is there a way to ensure that the render method waits on my changes or is it just the logic? I'm confused.
This here is one instance, I have 3 currently in my code..
// added this variable to state
this.state = {
verificationStatus: ''
}
// getStatus function
await Http.post({
url: '/api/id-status-check',
headers: {'user': localStorage.getItem('userId')}
})
.then((response)=>{
verificationStatus = response.identificationStatus
this.setState({verificationStatus: response.identificationStatus})
Then I'm trying to do something like this but the class is only removed after the page loads.
if (this.state.verificationStatus.toLowerCase() !== 'failed') {
dCheck.disabled = true
dCheck.classList.remove('btn-primary2')
This is my componentDidMount function where I attempt to run the same function to get the status before the initial render. And btw the logic works to produce the output but only after the page loads so I can see the button color change on load which is my concern.
componentDidMount() {
this.getStatus().then(r => {})
}

How to persist the view of a route in angular

Is there any way to persist the view of a page in angular so that as a User I can get the same view when I revisit the same route.
For eg. RouteA has a search bar and when user search something it loads results below, now if user has searched something on that page and
after that he leave that page and move to RouteB for some other operation. When he will revisit the page it should have the same view, that is searched term in the search bar and loaded data in the grid.
For your use case, you can save the FormGroup instance before the user navigates to a different route.
if (this.searchFormGroup.valid) {
this.searchService.setLastSearch(this.searchFormGroup);
}
where the definition for setLastSearch could be like
setLastSearch(searchFormGroup) {
this.lastSearch = searchFormGroup;
}
And when the user revisits the page, in the ngOnInit method check if there is already a saved search(the formGroup instance you saved before navigating).
this.lastSearch = this.searchService.getLastSearch();
. If the answer is yes use the patchValue to populate the current formGroup instance. Something like below
if (this.lastSearch) {
this.patchLastSearchedValue();
}
where the code for patchLastSearchedValue could be like
patchLastSearchedValue(): void {
Object.keys(this.lastSearch.value).forEach(name => {
this.searchFormGroup.controls[name].patchValue(
this.lastSearch.controls[name].value
);
});
}

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