0
votes

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;

enter image description here

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;
1
There is no this.state in functional components, only in class based. You can try passing down through props the value of documentId to UploadFile from AddArticle component.norbitrial

1 Answers

1
votes

As I mentioned in the comment section the main problem is there is no this.state in functional components, only in class based. You can try passing down through props the value of documentId to UploadFile from AddArticle component.

The solution could be by passing down through props the documentId as:

<UploadFile onChange={this.onChange} value={fileUrl} documentId={this.state.documentId} />

And modify the UploadFile component based on that:

function UploadFile(props) {
  const { documentId } = props

  // your component's implementation
}

Then simply you can use as documentId instead of this.state.documentId:

firebase.firestore().collection('articles').doc(documentId)