React setState {} not setting variable - reactjs

I have the following code for a main view and user login:
export class MainView extends React.Component {
constructor(){
super();
this.state = {
array: [],
user: null,
userData: {},
};
}
setUserData(user) {
if (user) {
this.setState({ userData: user.user });
console.log(user.user);
console.log (userData); /* errors in console */
} else {
console.log('userData not set');
}
}
onLoggedIn(authData) {
this.setState({ user: authData.user.Username });
/* setting localstorage key:item */
// console.log(authData);
localStorage.setItem('token', authData.token);
localStorage.setItem('user', authData.user.Username);
this.getArrayObjects(authData.token);
this.setUserData(authData);
}
using Passport to get the auth data and token. I don't get why it will log user.user in the setUserData function but will log an "undefined" error for the userData variable in the same function. Any ideas?

You are trying to access an undefined variable.
userData is present inside the state.
So you should access it with this.state.userData.
But even it you write console.log(userData);, it will print {} because in React, setState is asynchronous. Meaning, the state is not updated immediately.
If you want to check whether state has been update or not, check it like this.
this.setState({ userData: user.user }, () => {
console.log(this.state.userData);
});
The, second parameter to setState is a function that is called once state is successfully updated. So, inside there you can see the value in console.

You can't access the state directly as a var. You need to access the state, and the the properties:
console.log(state.userData);
Also, in your code, when you print state.userData, probably you will see the old value, since the setState is an async function.

Related

Setting the state in a component from localStorage

I'm building a multi-step form in React and one of my objectives is to save the user's input if they haven't finished filling in the form. I have saved the user's input in the browser's localStorage by using setItem().
The input fields set the local state which in turn get saved to the localStorage.
However, when the page is refreshed, I want to retrieve the data from localStorage and set the state variables from there so that it pre-fills the input fields with the saved data (if that makes sense)
I'm using setState() in componentDidMount() to do this, although I think that's creating an anti-pattern and I'm not entirely sure what that is. It works fine when I use UNSAFE_componentWillMount but I don't want to use a deprecated lifecycle method.
This is my code :
componentDidMount() {
this.formData = JSON.parse(localStorage.getItem('form'));
this.setState({
type: this.formData.type,
subtype: this.formData.subtype,
brand: this.formData.brand
})
}
the idea to use componentDidMount is correct. There is another anti-pattern.
Don't use this.formData = ... outside of component's constructor - https://reactjs.org/docs/react-component.html
Whole working example would look like this. I added callback after setState to show that loading & saving to localStorage actually works.
export default class Hello extends React.Component {
state = {
type: undefined,
subtype: undefined,
brand: 0,
}
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form')) ?? {};
if (formData) {
formData.brand += 5
this.setState({
type: formData.type,
subtype: formData.subtype,
brand: formData.brand,
}, () => {
console.log('newState', this.state)
localStorage.setItem('form', JSON.stringify(this.state))
})
}
}
render() {
return <h1>Hello {this.state.brand} </h1>
}
}
you can use constructor function if you do not want to retrieve local storage data in componentDidMount()
constructor(){
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}
Though I'd suggest to go with didMount.
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}

Can't set state in react to array of strings

I'm new to react and having a super hard time. My most recent problem is trying to set the state of 'favMovies' to an array of strings (movie IDs).
States:
export class MainView extends React.Component {
constructor() {
super();
this.state = {
movies: [],
favMovies: [],
user: null,
};
}
Setting states:
onLoggedIn(authData) {
console.log(authData);
console.log(authData.user.FavoriteMovies);
this.setState({
favMovies: authData.user.FavoriteMovies,
});
this.setState({ user: authData.user });
localStorage.setItem('token', authData.token);
localStorage.setItem('user', JSON.stringify(authData.user));
this.getMovies(authData.token);
this.getUsers();
}
I kind of understand that set state is async and doesn't happen until the next render. The part that I'm confused by is the 'user' that get's set after 'favMovies' works as expected, but 'favMovies' is undefined.
I know this is probly a dumb question, but I'm absolutely lost in react right now and struggling. Any help would be appreciated.
It's alaways better to use single setState if possible, because everytime you call setState it will re-render the view. One more tip for you, when you are dealing with object and arrays try to use spread operstor to assign to the state, instead of direct assignment.
onLoggedIn(authData) {
console.log(authData);
console.log(authData.user.FavoriteMovies);
this.setState({
favMovies: [...authData.user.FavoriteMovies],
user: authData.user
});
//this.setState({ user: authData.user });
localStorage.setItem('token', authData.token);
localStorage.setItem('user', JSON.stringify(authData.user));
this.getMovies(authData.token);
this.getUsers();
}
As we know react setState is asynchronous, state won't reflect immediately. We can use callback with setState where we can access updated state.
this.setState(
{
favMovies: [...authData.user.FavoriteMovies],
user: authData.user
// ...
},
() => {
this.doSomethingAfterStateUpdate();
}
);

Why won't my changes to state show in my componentDidMount lifecycle?

I am building an app using React and for my homepage, I set state in the componentDidMount lifecycle:
export default class HomePage extends Component {
state = {
posts: [],
token: '',
};
//Display posts when homepage renders
componentDidMount() {
//If token exists, run lifecycle event
if (this.props.location.state.token) {
this.setState({ token: this.props.location.state.token });
}
Axios.get('http://localhost:3000/api/posts/all')
.then((req) => {
this.setState({ posts: req.data });
})
.catch((err) => {
console.log(err.message);
throw err;
});
console.log(this.state);
}
However when I run the console log at the end of the lifecycle method, it shows posts and token as still being empty. I know they are being populated because the posts from the req.data show up in my JSX. Why does it show state being empty when I console log inside the method?
React setState is asynchronous!
React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component.
Think of setState() as a request rather than an immediate command to update the component.
this.setState((previousState, currentProps) => {
return { ...previousState, foo: currentProps.bar };
});

How to setState inside an api function

I have this code inside a class method:
ref
.orderByValue()
.equalTo(email)
.once("value", snapshot => {
if (snapshot.exists()) {
console.log(this);
this.setState({ signedUp: true });
} else {
ref.update({ [newKey]: email });
}
});
It's going to update my Firebase database on submit, unless the user has already signed up. A typeError says this.setState is not a function. But when I console log this, the console prints out the class. Logging this.state also prints out the state.
How do I fix this? I want to update my state from inside this function.
The best solution is to use functional component.
However if you want to use class component, this is probably a binding issue.
So if this is triggered inside function named func, you should bind this function inside the constructor.
...
constructor(props) {
...
this.func = this.func.bind(this);
...
}
func() {
...
ref
.orderByValue()
.equalTo(email)
.once("value", snapshot => {
if (snapshot.exists()) {
console.log(this);
this.setState({ signedUp: true });
} else {
ref.update({ [newKey]: email });
}
});
...
}
If you don't want to bind this function, you should use arrow function.
func = () => {
...
}

How to retrieve user data from fire store

I need to fetch the user data and display it. I am getting an error now that says
TypeError: this.unsubscribe is not a function
and when I initialise it as a normal variable like const db, then I get another error
Function CollectionReference.doc() requires its first argument to be of type non-empty string
import React from "react";
import { auth, firestore } from "../../firebase/firebase.utils";
export default class UserPage extends React.Component {
state = {
user: {}
};
unsubscribe = null;
componentDidMount() {
const user = auth.currentUser;
this.unsubscribe = firestore
.collection("users")
.doc(user)
.onSnapshot(doc => {
this.setState = {
user: doc.data()
};
});
}
componentWillMount() {
this.unsubscribe();
}
render() {
return (
<div>
<h1>{this.state.user.name}</h1>
</div>
);
}
}
when I insinalise it as a normal variable like const db
Not quite sure what you mean by this, but if you're getting an error about the type of unsubscribe, I suggest using console.log right before you call it to view its value.
Bear in mind that componentWillMount happens in the lifecycle before componentDidMount (hence the names will and did). I suspect that's one of your problems: you try to call unsubscribe before setting the value.
With regard to your other error about the doc call, it's likely referring to:
...
.collection("users")
.doc(user) <-- this line
.onSnapshot(doc => {
this.setState = {
us
...
As the error output states, that user variable (the first argument of doc) must be a string, and it can't be an empty string.
I don't see user anywhere in your code, so I expect that it's currently the value undefined. You could access this.state.user here, but I'd strongly advise against it since you subsequently set that state in the call (probably cause an infinite loop).
What is your end goal? What have you tried to resolve these two issues? Maybe adding that to your question would help us assist you better.

Resources