setState user property inside removeListener return undefined when i console.log() it inside component but when i check state in react developer tool user object from firebase it is there with actual value I want
state = {
coinList: {},
fav: [],
currentFavourite: '',
prices: [],
authed: false,
user: null
};
componentDidMount = () => {
this.removeListener = firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
authed: true,
user
});
} else {
this.setState({
authed: false
});
}
});
this.ref = base.syncState('fav', {
context: this,
state: 'fav',
asArray: true
});}
If your console.log statement is inside the removeListener, I'd suspect that state hasn't been updated by the time console.log is called.
setState is asynchronous, so it's been updated in the background whilst the next statements are being.
You can provide setState with a function or statement that is only called once setState is completed....
this.setState({ user }, this.someFunction())
or simply...
this.setState({ user }, console.log(this.state.user));
Related
Hi I am calling componentdidupdate in order to re-render my page with new ToDos like so
this.state = {
displayToDos: false,
displayLanding: true,
todo: {
title: '',
task: ''
},
todos: [],
createdToDo: null,
updateModal: false
}
}
async componentDidMount() {
this.fetchItems()
}
componentDidUpdate(prevState) {
if (prevState.todos !== this.state.todos) {
this.fetchItems()
}
}
fetchItems = async () => {
try {
const todos = await getItems()
this.setState({ todos })
console.log('set state of todos', this.state.todos)
} catch (err) {
console.error(err)
}
}
my intended behavior for my app is that the component mounts fetches the items and sets them to state, and then whenever I make a post, update or delete call the state of todos changes and it fires off the fetch call to re render the todos on the page. However I am getting an infinite loop, how do I fix this?
You can compare arrays like this:
let arraysEqual = JSON.stringify(todo) == JSON.stringify(todo1);
Or I will suggest you use this library Loadsh
you can use the .isEqual method to check the arrays.
I have a template that needs some non-reactive data in it from my Vuex store. However at the moment, I have to manually switch views to get the data to load. I am assuming I should not use mounted or created. If I use watch, then it basically becomes reactive again, and I only want to get this once.
data: function () {
return {
localreadmode: false,
myArray: null,
}
},
computed: {
...mapState({
myNodes: (state) => state.myNodes,
configPositions: (state) => state.configPositions,
configEmoji: (state) => state.configEmoji,
}),
nodes_filtered: function () {
return this.myNodes.filter((nodes) => {
return nodes.deleted == false
})
},
},
// this is to stop sync chasing bug
myArray: null,
created() {
this.$options.myArray = this.nodes_filtered
console.log(this.nodes_filtered)
// is empty unless I switch views
},
You could still use a watcher that runs only once via the vm.$watch API. It returns a method that can be called to stop watching the value, so your handler could invoke it when nodes_filtered[] is not empty.
export default {
mounted() {
const unwatch = this.$watch(this.nodes_filtered, value => {
// ignore falsy values
if (!value) return
// stop watching when nodes_filtered[] is not empty
if (value.length) unwatch()
// process value here
})
}
}
demo
So, I attempting to update some data from component every time the state in vuex not null. I set up an API routes with laravel that returns user information after they logged in.
API routes:
Route::group(['middleware' => ['auth:api']], function () {
Route::get('profil', 'Api\UserController#profil')->name('profile'); // will returning user info
}
Vuex:
export default new Vuex.Store({
state: {
token: localStorage.getItem('token') || "",
user: {}
},
getters: {
isAuth: state => {
return state.token != "" && state.token != null
}
},
mutations: {
SET_TOKEN(state, payload) {
state.token = payload
},
SET_AUTH_USER(state, payload) {
state.user = payload
}
}
})
So in my App.vue, in created method, I commit SET_AUTH_USER with the http response as the payload if the token was exist.
App.vue:
<template>
<div id="app-layout">
<section-advices></section-advices>
</template>
<script>
import SectionAdvices from "./SectionAdvices"
export default {
name: "app-layout",
components: {
SectionAdvices
},
created() {
if (this.$store.state.token !== (null || "")) { //check token in vuex state
this.$http
.get("/profil")
.then(res => {
if (res.status === 200) {
this.$store.commit("SET_AUTH_USER", res.data.data); //set $store.state.user with response
} else {
this.$store.commit("SET_AUTH_USER", null); // set with null
}
})
.catch(err => {
console.log(err);
});
}
}
}
</script>
so far, everything works fine. every time I refresh the page, as long as there's a token in my local storage, the user object will always have the user information.
SectionAdvices.vue:
<template>
<section class="advices">
<input type="text" v-model="name">
<input type="text" v-model="email">
</section>
</template>
<script>
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
name: "section-advices",
data(){
return{
name: null,
email: null
}
},
computed:{
...mapState(["user"]),
...mapGetters(["isAuth"]),
},
created() {
if(this.isAuth) { //its returning true. the codes below was executed
this.name = this.user.name // gives name data with "undefined"
this.form.email = this.user.email // it's "undefined" too
}
}
}
</script>
Both name and email in SectionAdvices component was set as "undefined" although in Vue Dev tools, the user object does have these values. Did I call the api in App.vue inside wrong life cycle?
Can you try to use getters to take state data? Because of lifecycles. Getters are setting first when component page rendering
I found th solution, which is:
as what #dreijntjens suggested, I added watcher in my "SectionAdvices.vue"
...
watch: {
// watching for store.state.user changes
user: {
handler: "fillForm", // call the method
immediate: true // this watcher will exec immediately after the comp. created
}
},
methods: {
fillForm() {
if(this.isAuth) { // this is not necessary, just want to make sure. Once isAuth === true, codes below will be executed
this.form.name = this.user.name
this.form.email = this.user.email
}
}
}
So, the real problem is, when SectionAdvices.vue created and fetching data, store.state.user still an empty object. My API calls runs after this step, which is pointless. So, it's true I need watcher, to see any changes in user state and update the local data inside its component after that.
I'm trying to unit test onClick the state of the props in my component clear.
I tried doing it this way:
props = {
attributeTableData: data,
clearMessage: onClickMethod,
reset: () => { },
resetAttributeTable: () => { },
statusMessage: {
messageType: 'message-success',
userMessage: 'Template has been saved successfully. Please wait …see your results display with the latest'
},
submitTemplateCreationStatus: () => { },
templateAttributeFormData: () => { },
templateFormSubmission: true,
templateAttributeFormSubmission: true,
templateFormData: () => { },
userRoles: new Set(['admin'])
};
let emptyStatusMessage = {};
actualComponent = shallow(<CreateTemplateResults { ...props } />);
actualComponent.instance().resetForms();
expect(onClickMethod.called).to.be.true;
expect(actualComponent.state('statusMessage')).to.eql(emptyStatusMessage)
But I get:
" TypeError: ShallowWrapper::state("statusMessage") requires that
state not be null or undefined"
You are creating a shallow render of <CreateTemplateResults />, but never passing actualComponent.setState(nextState). Therefore, when you are trying to access the state on your last line, it is throwing an error because state is null/undefined.
shallow().setState
I'm building a blog application that has an articles index page, and from there you can click on an article to see the article or edit the article.
If you're going from the index page to the edit page, it works just fine because I already have all the articles in state. But if I refresh after I've gone to the edit-article page, I no longer have all the articles in state.
This is a problem because I'm making an asynchronous recieveSingleArticle call in the componentDidMount of my edit-article page, then I setState so my form is prepopulated. There's a double render which causes an "Uncaught TypeError: Cannot read property 'title' of undefined" error, presumably during the first render before the article has been received into state.
class ArticleEdit extends React.Component {
constructor(props) {
super(props);
this.state = {title: "", body: "", imageFile: ""};
this.handleChange = this.handleChange.bind(this);
this.handlePublish = this.handlePublish.bind(this);
this.handleFile = this.handleFile.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
componentDidMount() {
const { article, requestSingleArticle } = this.props;
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
...
I tried wrapping my async calls inside of an "if (this.props.article)" but that didn't work. Is there a best way of dealing with this type of problem? Any advice greatly appreciated!
UPDATE:
Another solution that works is to have a componentDidUpdate in addition to componentDidMount. check in componentDidMount if this.props.article exists and if so, setState. And in componentDidUpdate, wrap the setState in the following conditional:
if (!prevProps.article && this.props.article)
Just check if the article is present in the props before calling async action
componentDidMount() {
const { article, requestSingleArticle } = this.props;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
Since you are not getting any render from this method , it means that the props are not yet obtained in the life cycle method componnetDidMount. So instead you can use componentWillReceiveProps like this
componentWillReceiveProps(nextProp) {
// this line here will check the article props' status so that
// we will not use setState each time we get a prop
if (this.props.article === nextProp.article) return;
// rest is just the same code from above
const { article, requestSingleArticle } = nextProp;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}