I am developing a blog page where users can create posts. Upon clicking the “Create Post” button, the post is successfully added to the database and displayed on the blog. However, I am encountering an error during this process. The page turns red and I receive an error message saying “Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it’s defined in, or you might have mixed up default and named imports.”
To create a post, I have implemented the following steps:
On the server-side (index.js), I have created routes for registering, logging in, and creating posts. I have also defined a middleware for file upload using multer.
On the client-side, I have an IndexPage component that fetches and displays the posts.
I have a CreatePost component where users can enter the post details, including a file upload using ReactQuill and the input type “file”.
I expected that upon creating a post, it would be successfully added to the database and displayed on the blog without any errors. However, the encountered error turns the page red and prevents a smooth experience.
I would appreciate any insights or suggestions on how to identify and fix the error in order to successfully create a post without encountering any issues. Thank you in advance for your help.
/////////////////////
Index.js
/////////////////////
const express = require('express');
const mongoose = require('mongoose');
const User = require('./models/User');
const Post = require('./models/Post');
const cors = require('cors');
const bcrypt = require('bcryptjs');
const app = express();
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const multer = require('multer');
const uploadMiddleware = multer({ dest: 'uploads/' });
const fs = require('fs');
const salt = bcrypt.genSaltSync(10);
const secret = 'asdfe45we45w345wegw345werjktjwertkj';
app.use(cors({ credentials: true, origin: 'http://localhost:3000' }));
app.use(express.json());
app.use(cookieParser());
mongoose.connect('mongodb+srv://g00366442:[email protected]/?retryWrites=true&w=majority');
app.post('/register', async (req, res) => {
const { username, password } = req.body;
try {
const userDoc = await User.create({
username,
password: bcrypt.hashSync(password, salt),
});
res.json(userDoc);
} catch (e) {
console.log(e);
res.status(400).json(e);
}
});
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const userDoc = await User.findOne({ username });
const passOk = bcrypt.compareSync(password, userDoc.password);
if (passOk) {
// logged in
jwt.sign({ username, id: userDoc._id }, secret, {}, (err, token) => {
if (err) throw err;
res.cookie('token', token).json({
id: userDoc._id,
username,
});
});
} else {
res.status(400).json('wrong credentials');
}
});
app.get('/profile', (req, res) => {
const { token } = req.cookies;
jwt.verify(token, secret, {}, (err, info) => {
if (err) throw err;
res.json(info);
});
});
app.post('/logout', (req, res) => {
res.cookie('token', '').json('ok');
});
app.post('/post', uploadMiddleware.single('file'), async (req, res) => {
const { originalname, path } = req.file;
const parts = originalname.split('.');
const ext = parts[parts.length - 1];
const newPath = path + '.' + ext;
fs.renameSync(path, newPath);
const { token } = req.cookies;
jwt.verify(token, secret, {}, async (err, info) => {
if (err) throw err;
const { title, summary, content } = req.body;
const postDoc = await Post.create({
title,
summary,
content,
cover: newPath,
author: info.id,
});
res.json(postDoc);
});
});
app.get('/post', async (req, res) => {
res.json(await Post.find());
});
app.listen(4000);
/////////////////////////////////////////////////////
IndexPage.js
///////////////
import Post from "../Post";
import { useEffect, useState } from "react";
export default function IndexPage() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('http://localhost:4000/post').then(response => {
response.json().then(posts => {
setPosts(posts);
});
});
}, []);
return (
<>
{posts.length > 0 && posts.map(post => (
<Post key={post.id} {...post} />
))}
</>
);
}
/////////////////////////////
CreatePost.js
//////////////////
import React, { useState, Navigate } from "react";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
const modules = {
toolbar: [
[{ header: [1, 2, false] }],
["bold", "italic", "underline", "strike", "blockquote"],
[{ list: "ordered" }, { list: "bullet" }, { indent: "-1" }],
["link", "image"],
["clean"]
]
};
const formats = [
'header',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image'
];
export default function CreatePost() {
const [title, setTitle] = useState("");
const [summary, setSummary] = useState("");
const [content, setContent] = useState("");
const [files, setFiles] = useState("");
const [redirect, setRedirect] = useState(false);
async function createNewPost(ev) {
const data = new FormData();
data.set('title', title);
data.set('summary', summary);
data.set('content', content);
data.set('file', files[0]);
ev.preventDefault();
const response = await fetch('http://localhost:4000/post', {
method: 'POST',
body: data,
credentials: 'include',
});
if (response.ok) {
setRedirect(true);
}
}
if (redirect) {
return <Navigate to={'/'} />;
}
return (
<form onSubmit={createNewPost}>
<input
type="title"
placeholder={'Title'}
value={title}
onChange={ev => setTitle(ev.target.value)}
/>
<input
type="summary"
placeholder={'Summary'}
value={summary}
onChange={ev => setSummary(ev.target.value)}
/>
<input type="file" onChange={ev => setFiles(ev.target.files)} />
<ReactQuill
value={content}
onChange={value => setContent(value)}
modules={modules}
formats={formats}
theme="snow"
/>
<button type="submit">Create Post</button>
</form>
);
}