I have a CMS where I create an article with a title, the content and an image. I then display all of that in the homepage. The images are being stored in Firebase Storage and I manage to do that successfully (when I check storage, they're there), however as soon as I submit the form, I see this error:
Unhandled Rejection (TypeError): Cannot read property 'state' of undefined
I'm really new to this so if you know what's wrong please be as specific as possible. I'm still learning how to pass props and all of that.
UploadFile.js
import React from "react";
import firebase from '../Firebase';
function UploadFile() {
const [fileUrl, setFileUrl] = React.useState(null);
const onFileChange = async (e) => {
const file = e.target.files[0]
const storageRef = firebase.storage().ref()
const fileRef = storageRef.child(`/articleImages/${file.name}`)
await fileRef.put(file);
setFileUrl(await fileRef.getDownloadURL().then(fileUrl => {
firebase.firestore().collection('articles').doc(this.state.documentId).update({
fileUrl: fileUrl
})
.then(() => {
setFileUrl('')
})
} ));
};
return (
<>
<input type="file" onChange={onFileChange} />
<div>
<img width="100" height="100" src={fileUrl} alt=''/>
</div>
</>
);
}
export default UploadFile;
This is the form where I've added the UploadFile component:
AddArticle.js
import React, { Component } from "react";
import firebase from "../Firebase";
import UploadFile from "../components/UploadFile";
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection("articles");
this.state = {
title: "",
content: "",
fileUrl: "",
};
}
onChange = (e) => {
const state = this.state;
state[e.target.name] = e.target.value;
this.setState(state);
};
onSubmit = (e) => {
e.preventDefault();
const { title, content, fileUrl } = this.state;
this.ref
.add({
title,
content,
fileUrl,
})
.then((docRef) => {
this.setState({
title: "",
content: "",
fileUrl: "",
documentId: docRef.id,
});
this.props.history.push("/");
})
.catch((error) => {
console.error("Error adding document: ", error);
});
};
render() {
const { title, content, fileUrl } = this.state;
return (
<div className="container">
<br></br>
<br></br>
<br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">Create a new article</h3>
</div>
<br></br>
<br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label for="title">Title:</label>
<input
type="text"
className="form-control"
name="title"
value={title}
onChange={this.onChange}
placeholder="Title"
/>
</div>
<div className="form-group">
<label for="content">Content:</label>
<textArea
className="form-control"
name="content"
onChange={this.onChange}
placeholder="Content"
cols="80"
rows="20"
>
{content}
</textArea>
</div>
{/* <input type="file" onChange={this.onFileChange} /> */}
<UploadFile onChange={this.onChange} value={fileUrl} />
<button type="submit" className="btn btn-success">
Submit
</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
And finally, I have a homepage where I display the image and the other data from the form:
ViewAllArticles.js
import React, { Component } from "react";
import firebase from "../Firebase";
import { Link } from "react-router-dom";
class ViewAllArticles extends Component {
constructor(props) {
super(props);
this.ref = firebase.firestore().collection("articles");
this.unsubscribe = null;
this.state = {
articles: [],
};
}
onCollectionUpdate = (querySnapshot) => {
const articles = [];
querySnapshot.forEach((doc) => {
const { title, content, fileUrl } = doc.data();
articles.push({
key: doc.id,
doc, // DocumentSnapshot
title,
content,
fileUrl,
});
});
this.setState({
articles,
});
};
componentDidMount() {
this.unsubscribe = this.ref.onSnapshot(this.onCollectionUpdate);
}
render() {
return (
<div className="container">
<br></br>
<br></br>
<br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">All articles</h3>
</div>
<br></br>
<br></br>
<br></br>
<div className="panel-body">
<table className="table table-stripe">
<thead>
<tr>
<th>Title</th>
<th>Content</th>
<th>Image</th>
</tr>
</thead>
<tbody>
{this.state.articles.map((article) => (
<tr>
<td>
<Link to={`/editArticle/${article.key}`}>
{article.title}
</Link>
</td>
<td>{article.content}</td>
<td>
<img
width="100"
height="100"
src={article.fileUrl}
onError={(e) => {
e.target.onerror = null;
e.target.src =
"https://images.unsplash.com/photo-1597759261513-474270ffae0f?ixlib=rb-1.2.1&auto=format&fit=crop&w=2238&q=80";
}}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default ViewAllArticles;
this.state
in functional components, only in class based. You can try passing down throughprops
the value ofdocumentId
toUploadFile
fromAddArticle
component. – norbitrial