I'm using marked.js to create posts using markdown. The posts are created successfully with markdown and the html is being displayed however when I update / edit posts, sanitizedHtml is not being updated, it still contains the old content.
The other fields are being updated but sanitizedHtml is relying on the value from body and then converting it to html and sanitizing it in the back end.
Is sanitizedHtml not updating because I haven't set body to it in React and how can I solve it and get sanitizedHtml to update with the new value in body?
Post.js model
// ...
const marked = require("marked");
const createDomPurify = require("dompurify");
const { JSDOM } = require("jsdom");
const domPurify = createDomPurify(new JSDOM().window);
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
// ...
sanitizedHtml: {
type: String,
required: true,
},
});
postSchema.pre("validate", function (next) {
// markdown - sanitize and convert to html
if (this.body) {
this.sanitizedHtml = domPurify.sanitize(marked(this.body));
}
next();
});
EditPost.tsx
const EditPost = ({ match } : { match: any }) => {
const [values, setValues] = useState({
title: "",
body: "",
error: "",
sanitizedHtml: "",
updatedPost: "",
});
const [post, setPost] = useState({ title: values.title, body: values.body, sanitizedHtml: values.sanitizedHtml });
const { token } = isAuthenticated();
const { title, body, error, updatedPost, sanitizedHtml } = values;
const init = (slug: string, id: number) => {
read(slug, id).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({ ...values, title: data.title, body: data.body, sanitizedHtml: data.sanitizedHtml });
setPost({ title: values.title, body: values.body, sanitizedHtml: values.sanitizedHtml });
}
});
};
useEffect(() => {
const id = match.params.id;
const slug = match.params.slug
init(slug, id);
}, []);
useEffect(() => {
setPost({ ...values });
}, [values.title, values.body, values.sanitizedHtml]);
const handleChange = (name: string) => (event: any) => {
setValues({ ...values, [name]: event.target.value });
};
const clickSubmit = (event: any) => {
event.preventDefault();
setValues({ ...values, error: "" });
setPost({ title: values.title, body: values.body, sanitizedHtml: values.sanitizedHtml });
editPost(match.params.userId, match.params.id, token, post).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues({
...values,
title: data.title,
body: data.body,
sanitizedHtml: data.sanitizedHtml,
updatedPost: data.title,
error: "",
});
console.log(post);
console.log(data);
}
});
};
const newPostForm = () => (
<form onSubmit={clickSubmit}>
<div>
<input
onChange={handleChange("title")}
type="text"
name="title"
value={title}
/>
</div>
<div>
<textarea
onChange={handleChange("body")}
value={body}
name="body"
/>
</div>
<button type="submit">
Publish
</button>
</form>
);
return (
<>
<div>
{newPostForm()}
</div>
</>
);
};
export default EditPost;
controllers/posts.js
exports.edit = (req, res) => {
const id = req.params.id;
if (!ObjectID.isValid(id))
return res.status(400).send(`No post with given id: ${id}`);
const { title, body, sanitizedHtml } = req.body;
const updatedPost = { title, body, sanitizedHtml };
Post.findByIdAndUpdate(
id,
{
$set: updatedPost,
},
{ new: true },
(error, data) => {
if (error) {
return error;
} else {
res.send(data);
console.log(updatedPost);
}
}
);
};
Solved it! I needed to use marked.js in the front end too.
I added the following code to convert the text area value to markdown and set that to
sanitizedHtmlin state: