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
sanitizedHtml
in state: