问题
i am trying to use Draft.js in the Blog app i am creating. It all seems fine when saving data to the database and getting it back, just i cannot seem to get createWithContent to work. I am going to show you most of my code for clarity, apologise if it may seem too much.
This is how i set-up the project:
this is the post model
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
image: { type: String, required: true },
images: [String],
paragraph: { type: String, required: true },
},
{ timestamps: true }
);
const Post = mongoose.model('Post', postSchema);
export default Post;
this is the router to get and modify the post:
postRouter.get(
'/:id',
expressAsyncHandler(async (req, res) => {
const post = await Post.findOne({ _id: req.params.id });
if (post) {
res.send(post);
} else {
res.status(404).send({ message: 'Post Not Found.' });
}
})
);
postRouter.put(
'/:id',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const postId = req.params.id;
const post = await Post.findById(postId);
if (post) {
post.title = req.body.title;
post.image = req.body.image;
post.images = req.body.images;
post.paragraph = req.body.paragraph;
const updatedPost = await post.save();
if (updatedPost) {
return res
.status(200)
.send({ message: 'Post Updated', data: updatedPost });
}
}
return res.status(500).send({ message: ' Error in Updating Post.' });
})
);
these are the Redux actions to get the details of the post and update it:
export const updatePost = (post) => async (dispatch, getState) => {
try {
dispatch({ type: POST_UPDATE_REQUEST, payload: post });
const {
userSignin: {
userInfo: { token },
},
} = getState();
const { data } = await Axios.put(`/api/posts/${post._id}`, post, {
headers: {
Authorization: `Bearer ${token}`,
},
});
dispatch({ type: POST_UPDATE_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: POST_UPDATE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const detailsPost = (postId) => async (dispatch) => {
try {
dispatch({ type: POST_DETAILS_REQUEST, payload: postId });
const { data } = await Axios.get(`/api/posts/${postId}`);
dispatch({ type: POST_DETAILS_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: POST_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
and the respective reducer:
export const postDetailsReducer = (
state = { loading: true, post: { comments: [] } },
action
) => {
switch (action.type) {
case POST_DETAILS_REQUEST:
return { ...state, loading: true };
case POST_DETAILS_SUCCESS:
return { loading: false, post: action.payload };
case POST_DETAILS_FAIL:
return { ...state, loading: false, error: action.payload };
case POST_DETAILS_RESET:
return { post: { comments: [] } };
default:
return state;
}
};
export const postUpdateReducer = (state = { post: {} }, action) => {
switch (action.type) {
case POST_UPDATE_REQUEST:
return { loading: true };
case POST_UPDATE_SUCCESS:
return { loading: false, success: true, post: action.payload };
case POST_UPDATE_FAIL:
return { loading: false, error: action.payload };
case POST_UPDATE_RESET:
return { post: {} };
default:
return state;
}
};
and these are the reducers in the Redux store:
userDetails: userDetailsReducer,
userUpdate: userUpdateReducer,
and as last i have the screen where i am trying to update the post. Here i've set the following:
const postId = props.match.params.id;
const dispatch = useDispatch();
const [id, setId] = useState('');
const [title, setTitle] = useState('');
const [image, setImage] = useState('');
const [images, setImages] = useState([]);
const [uploading, setUploading] = useState(false);
const postDetails = useSelector((state) => state.postDetails);
const { loading, post, error } = postDetails;
const postUpdate = useSelector((state) => state.postUpdate);
const {
error: errorUpdate,
loading: loadingUpdate,
success: successUpdate,
} = postUpdate;
and
const editorContent = EditorState.createEmpty();
const [editorState, setEditorState] = useState({ editorState: editorContent });
const handleEditorChange = (editorState) => { setEditorState({ editorState }) }
then
useEffect(() => {
if (successUpdate) {
dispatch({ type: POST_UPDATE_RESET });
props.history.push(`/postlist`);
}
if (!post.title) {
dispatch(detailsPost(postId));
} else {
setId(post._id);
setTitle(post.title);
setImage(post.image);
setImages(post.images);
}
return () => {
//
};
}, [post, successUpdate, dispatch, props.history, postId]);
here with the submitHandler i dispatch a Redux action that sends the data to the database:
const submitHandler = (e) => {
e.preventDefault();
dispatch(
updatePost({
_id: id,
title,
image,
images,
paragraph: JSON.stringify(convertToRaw(editorState.editorState.getCurrentContent()))
})
);
};
and my Editor at the bottom of the following:
<return (
<div>
<form className="form" onSubmit={submitHandler}>
<div>
<h1>Edit Post {id}</h1>
</div>
{loadingUpdate && <LoadingBox />}
{errorUpdate && <MessageBox variant="error">{errorUpdate}</MessageBox>}
{loading && <LoadingBox />}
{error && <MessageBox variant="error">{error}</MessageBox>}
{post.title && (
<>
<div>
<label htmlFor="name">Title</label>
<input
id="name"
type="text"
placeholder="Enter title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div>
<label htmlFor="image">Image Url</label>
<input
id="image"
type="text"
placeholder="Enter image url"
value={image}
onChange={(e) => setImage(e.target.value)}
/>
</div>
<div>
<div />
<div>
<button
onClick={() => props.history.push('/postlist')}
type="button"
>
Back
</button>{' '}
<button className="primary" type="submit">
Update
</button>
</div>
</div>
<div>
<label htmlFor="paragraph">Paragraph</label>
<Editor
editorState={editorState.editorState}
onEditorStateChange={handleEditorChange}
/>
</div>
</>
)}
</form>
</div>
)
Now, the paragraph (along with the other data for which i am not using a text editor) is successfully saved in the database and looks like this ( after JSON.stringify(convertToRaw() ):
{"blocks":[{"key":"20f7q","text":" Hello World","type":"unstyled","depth":0,"inlineStyleRanges":[{"offset":1,"length":3,"style":"bgcolor-yellow"}],"entityRanges":[],"data":{}}],"entityMap":{}}
What i want though is not an Empty editor but i want to be abe to edit an existing paragraph. Therefore i change the editorContent to the following:
const editorContent = post ?
EditorState.createWithContent(convertFromRaw(JSON.parse(post.paragraph))) :
EditorState.createEmpty();
It all seems fine to me, and i've even console.logged post coming from Redux store to check that i actually have data in it and that paragraph is included. Therefore i don't understand where the following error is coming from:
A cross-origin error was thrown. React doesn't have access to the actual error object in development.
I even added the following lines to my code to take care of the cross-origin:
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
next();
});
To recap, it seems like i am able to create an empty editor and send the data to the database, but i get an error when i try to createWithContent eventhough the data i am trying to use does exist! I really don't know what to try because everything looks fine to me so any idea or suggestion would be highly appreciated. Many Thanks
来源:https://stackoverflow.com/questions/64808416/draft-js-createwithcontent-or-convertfromraw-throwing-error