React - Cannot get property setState of null - reactjs

I am intending to get snapshot val from Firebase within my React component. I want to get the values based on init of the component and attach a listener for changes.
class ChatMessages extends Component {
constructor(props) {
super(props);
this.state = {
messages: [],
};
this.getMessages = this.getMessages.bind(this);
}
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
messagesRef.on('value', function(snapshot) {
this.setState({ messages: snapshot.val() });
});
}
componentDidMount() {
this.getMessages();
}
render() {
return (
<div className="container">
<ul>
<li>Default Chat Message</li>
{ this.state.messages }
</ul>
</div>
);
}
}

This is because 'this' is losing its context. So that, 'this.setState' is being undefined. You can have a reference for the actual 'this' via a variable called 'that'.
class ChatMessages extends Component {
constructor(props) {
super(props);
this.state = {
messages: [],
};
this.getMessages = this.getMessages.bind(this);
}
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
let that = this
messagesRef.on('value', function(snapshot) {
// here
that.setState({ messages: snapshot.val() });
});
}
componentDidMount() {
this.getMessages();
}
render() {
return (
<div className="container">
<ul>
<li>Default Chat Message</li>
{ this.state.messages }
</ul>
</div>
);
}
}
Or if possible, you can use arrow function, which keeps its context.
getMessages(event) {
const messagesRef = firebase.database().ref('messages');
// here
messagesRef.on('value', snapshot => {
// here
that.setState({ messages: snapshot.val() });
});
}

Related

How to use onload in react?

To run the imagesrore function onload I have to call <img src="image_7.jpg" className="hide" alt="image_7.jpg"/> image but actually there is no use of this line and if I remove this onload doesn't work and function is not called. So how can I call the imagestore() onload in react.
class PicturesList extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesarray: []
};
this.imagestore = this.imagestore.bind(this);
}
render() {
return (
<div>
<div onLoad= {() => this.imagestore()}>
<img src="image_7.jpg" className="hide" alt="image_7.jpg"/>
// To run the imagesrore function onload I have to call this image but actually there is no use of this line and if I remove this onload doesn't work and function is not called
</div>
<Gallery url={this.state.imagesarray}/>
</div>
);
}
imagestore()
{
const imgUrls=this.props.apikeys;
const objarr = Object.values(imgUrls);
this.setState({
imagesarray: objarr
});
}
}
what I want
class PicturesList extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesarray: []
};
this.imagestore = this.imagestore.bind(this);
}
render() {
return (
<div>
<div onLoad= {() => this.imagestore()}>
// but when I did this imagestore() function not called
</div>
<Gallery url={this.state.imagesarray}/>
</div>
);
}
imagestore()
{
const imgUrls=this.props.apikeys;
const objarr = Object.values(imgUrls);
this.setState({
imagesarray: objarr
});
}
}
Instead of rendering the image which you dont want, you could simply load it in componentDidMount like
class PicturesList extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesarray: []
};
this.imagestore = this.imagestore.bind(this);
}
componentDidMount() {
const img = new Image();
img.onload =() => {
// image has been loaded
this.imagestore()
};
img.src = 'image_7.jpg';
}
render() {
return (
<div>
</div>
<Gallery url={this.state.imagesarray}/>
</div>
);
}
imagestore() {
const imgUrls=this.props.apikeys;
const objarr = Object.values(imgUrls);
this.setState({
imagesarray: objarr
});
}
}
The above solution is just to call imageStore once an image is loaded. However if what you intend is to call imageStore when the component has fully loaded,just trigger this.imageStore() in componentDidMount
class PicturesList extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesarray: []
};
this.imagestore = this.imagestore.bind(this);
}
componentDidMount() {
this.imagestore()
}
render() {
return (
<div>
</div>
<Gallery url={this.state.imagesarray}/>
</div>
);
}
imagestore() {
const imgUrls=this.props.apikeys;
const objarr = Object.values(imgUrls);
this.setState({
imagesarray: objarr
});
}
}
Use useEffect() in React. You would use it like this:
useEffect(()=>{
**INSERT CODE YOU WANT RUN ON LOAD HERE **
}, [])
Remember to import useEffect as well with
import React, { useEffect } from 'react'
Your onLoad function should be used at the <img> tag, not the <div/>.

React: Issues with Conditional Rendering

In my React-App, i use the Firebase SDK. If a user wants to reset his password, he will be redirected to a page within my app. If the code is valid, the component <PWResetConfirmForm /> should be rended. If the code is invalid, the component <PWResetOutdatedForm /> is to be rendered.
My Page Component looks like this:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset(code)
.then(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
})
.catch(function () {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
})
}
render() {
return (
<div>
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage
When i try to run, i get a blank page and not error.
Where is my issue and how can i fix that?
Thank you very much for your help and for your time
You will not be able to return JSX from within then()/catch() of auth.doVerfiyPasswordReset() like that. You can instead approach this by taking advantage of React.Component lifecycle method componentDidMount and using setState() to manipulate state properties for conditional rendering. I've added state properties to the component, one to track whether loading (API call has completed) and one to track whether the call was a success (then) or failure (catch). These properties are used to conditionally generate JSX content for rendering. This is assuming that verfiyResetPassword() is intended to run when the component is first mounted, instead of every time render() is called:
class App extends Component {
constructor() {
super();
this.state = {
isResetVerified: null,
loading: true
};
}
componentDidMount() {
this.verfiyResetPassword();
}
verfiyResetPassword() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
auth.doVerfiyPasswordReset('foobar')
.then(() => {
this.setState({
...this.state,
isResetVerified: true,
loading: false
});
})
.catch(() => {
this.setState({
...this.state,
isResetVerified: false,
loading: false
});
})
}
getContent() {
if (this.state.loading) {
return (
<div>Loading...</div>
);
} else {
if (this.state.isResetVerified) {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetConfirmForm></PWResetConfirmForm>
</div>
);
} else {
return (
<div className="HomePage-Main">
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
}
Here is a basic example in action.
Also, in the constructor this.verfiyResetPassword = this.verfiyResetPassword.bind(this); would only be needed if verfiyResetPassword() is executed by a DOM event such as button onClick or similar.
Hopefully that helps!
I could still fix the error myself:
class PWResetConfirmPage extends Component {
constructor(props) {
super(props);
this.state = {
isValid: false,
code: "",
};
this.verfiyResetPassword = this.verfiyResetPassword.bind(this);
}
componentDidMount() {
const params = (new URL(`http://dummy.com${this.props.location.search}`)).searchParams;
const code = params.get("oobCode")
this.setState({code:code})
auth.doVerfiyPasswordReset(code)
.then(() => {
this.setState({
...this.state,
isValid: true,
});
})
.catch(() => {
this.setState({
...this.state,
isValid: false,
});
})
}
verfiyResetPassword() {
if (this.state.isValid) {
return (
<div>
<TopBar></TopBar>
<PWResetConfirmForm code={this.state.code}></PWResetConfirmForm>
</div>
);
} else {
return (
<div>
<TopBar></TopBar>
<PWResetOutdatedForm></PWResetOutdatedForm>
</div>
);
}
}
render() {
return (
<div className="HomePage-Main">
{this.verfiyResetPassword()}
</div>
);
}
}
export default PWResetConfirmPage

React this.state is undefined in component function

function typeContactGetter is binded to this and everything is working, the only issue is in the functions return on the <li> element, I am trying to set a className coming from state and it returns undefined for this.state.
Why is this happening?
Thanks,
Bud
component
class ContactType extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"),
friendContacts: this.typeContactGetter("Friends")
};
this.typeContactGetter = this.typeContactGetter.bind(this);
this.handleClick = this.handleClick.bind(this);
this.hideList = this.hideList.bind(this);
}
handleClick = (event) => {
event.preventDefault();
console.log('clicked, state: ' + this.state.hiddenList);
};
hideList = () => {
console.log("this is hidelist: " + this.state.hiddenList);
if (this.state.hiddenList === true){
this.setState({
hiddenList: false
});
}
this.setState({
hiddenList: !this.state.hiddenList
});
};
typeContactGetter = (name) => {
console.log(this.state);
for (let contact of CONTACTS) {
if (contact.name === name) {
return (
<li className={this.state.hiddenList ? 'hidden' : ''} onClick={this.handleClick} key={contact.id.toString()}>
{contact.contacts.map(value => {
if (value.type === "Contact") {
return (
<a key={value.id.toString()} href="#">{value.name}</a>
);
}
})
}
</li>
);
}
}
};
render() {
return (
<ContactView familyContacts={this.state.familyContacts} friendContacts={this.state.friendContacts} hideList={this.hideList}/>
);
}
}
export default ContactType;
That's because you call typeContactGetter in the constructor before the state is actually created.
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
familyContacts: this.typeContactGetter("Family"), // hey, but we are actually creating the state right now
friendContacts: this.typeContactGetter("Friends")
};
}
Why do you want to keep a component list in the state? Maybe it is better to pass them directly:
constructor(props) {
super(props);
this.state = {
date: new Date(),
hiddenList: false,
};
}
....
<ContactView familyContacts={this.typeContactGetter("Family")} friendContacts={this.typeContactGetter("Friends")} hideList={this.hideList}/>
btw you don't need to bind function as they are bound already by arrow functions.

React setState of array of objects

I have an array of 10 objects (Lets call them "Blogs") which contain title, description and image-URL properties. I need to wrap each of the properties in HTML tags and export them all so they all load on a webpage together.
With my current code, I am only getting 1 of the objects in the current state loading on the page. How do I get all the objects in the same state?
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
title: [],
description: [],
image: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
const blogs = data.data;
var component = this;
for(var i in blogs) {
component.setState({
title: blogs[i].title,
description: blogs[i].description,
image: blogs[i].image,
loading: false
});
}
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<h2>New Blogs:</h2>
<h3>{this.state.title}</h3>
<em>{this.state.description}</em>
<img src={this.state.image}></img>
</div>
);
}
}
export default NewBlogs
I haven't run/test this but try something like this
The API call appears to return a list of objects. If so just set state once the xhr completes and set loading false once.
In the react render() is where you could iterate over your list. The easiest way to do that is with '.map()'. You then simply return react elements for each object in your list.
Also let's rename 'component' to 'list'
class NewBlogs extends React.Component {
constructor(props) {
this.state = {
list: [],
loading: true
};
}
componentDidMount() {
axios.get('/new-blogs').then(data => {
// const blogs = data.data;
// var component = this;
this.setState({list: data.data, loading: false })
// for(var i in blogs) {
// this.setState({
// title: blogs[i].title,
// description: blogs[i].description,
// image: blogs[i].image,
// loading: false
// });
// }
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
{this.state.list.map(e => (
<h2>New Blogs:</h2>
<h3>{e.title}</h3>
<em>{e.description}</em>
<img src={e.image}></img>
))}
</div>
);
}
}
export default NewBlogs

ReactJS show loader on child component update

I have seen a lot loader plugins that work for the Mount life cycle but none for the update part and I wonder how to handle it?
What I tried was following setup for parent:
class App extends React.Component {
constructor() {
super()
this.state = {loader_wrap:false};
this.hideLoader = this.hideLoader.bind(this);
this.showLoader = this.showLoader.bind(this);
}
hideLoader(){
this.setState({loader_wrap: false});
}
showLoader() {
this.setState({loader_wrap: true});
}
render() {
var loaderStyle;
if (this.state.loader_wrap) {
loaderStyle = {display:"block"};
} else {
loaderStyle = {display:"none"};
}
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
</div>
)
}
}
And this is the child calling the methods:
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
});
}
componentDidMount() {
this.props.hideLoader();
}
componentDidUpdate() {
this.props.hideLoader();
}
componentWillReceiveProps(values){
this.setState({results:values.results});
}
render() {
return (
/*stuff to be returned*/
)
}
}
I also tried to use the Will methods .. which worked even worser :D
Any ideas how to implement this? I use react with flux but don't now how to use it in this case ..
Why not just call hideLoader() in the callback of the action's promise?
class Childextends React.Component {
constructor() {
super();
this.state = {results:[]};
this.calculate = this.calculate.bind(this);
}
calculate(dict) {
this.props.showLoader();
Actions.action(dict)
.then(results => {
this.setState({results: results});
})
.catch((err) => {
var errResp = JSON.parse(err.response);
console.log(errResp);
this.setState({responseErrors: errResp});
})
.then(() => {
this.props.hideLoader();
});
}
render() {
return (
/*stuff to be returned*/
)
}
}
Edit: A different approach to the parent component as well - rather than hiding the element with a style, just don't render it if it isn't required.
render() {
return (
<div>
<div id="content">
{React.cloneElement(content, {
hideLoader: this.hideLoader,
showLoader: this.showLoader
})}
</div>
{this.state.loader_wrap &&
<div id="loader-wrap" style={loaderStyle}>
<img className="loader hidden-sm hidden-xs" src='source/file/'>
</div>
}
</div>
)
}

Resources