juni
TeamProject_뉴스피드 프로젝트 본문
뉴스피드를 진행하며 게시글 CRUD를 맡았는데 Quill에디터를 쓰고 supabase DB를 내가 주로 다루다보니 UI 및 코드 가독성 향상을 위해 개선을 함
import { useState, useRef } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { createPost } from '../../redux/slices/postSlice';
import { supabase } from '../../service/supabase';
import { Quill } from 'react-quill';
import 'react-quill/dist/quill.snow.css';
import {
Wrapper,
Container,
Header,
Title,
// Subtitle,
Form,
Label,
Input,
StyledReactQuill,
ButtonContainer,
Button
} from './WritePostPage.styles';
const ImageBlot = Quill.import('formats/image');
ImageBlot.className = 'custom-image';
Quill.register(ImageBlot, true);
const WritePostPage = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [imageURL, setImageURL] = useState('');
const [previewImageURL, setPreviewImageURL] = useState(null);
const quillRef = useRef(null);
const imageInputRef = useRef(null);
const handleCreatePost = () => {
if (title && content) {
dispatch(createPost({ title, content, imageURL: imageURL }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
async function handleImageInputChange(files) {
const [file] = files;
if (!file) {
setPreviewImageURL(null);
return;
}
const previewURL = URL.createObjectURL(file);
setPreviewImageURL(previewURL);
const { data } = await supabase.storage.from('avatars').upload(`avatar_${Date.now()}.png`, file);
const imageURL = `https://supabase.com/dashboard/project/dkodekduyiphnphkezzv/storage/buckets/avatars/${data.path}`;
setImageURL(imageURL);
}
const handleImageButtonClick = (e) => {
e.preventDefault();
imageInputRef.current.click();
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['underline', 'strike', 'blockquote', 'code-block'],
[{ color: [] }, { background: [] }],
['image']
]
};
return (
<Wrapper>
<Container>
<Header>
<Title>메인 이미지 미리보기</Title>
<br />
{previewImageURL && <img src={previewImageURL} alt="Preview" style={{ maxWidth: '200px' }} />}
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleCreatePost}>업로드</Button>
<Button onClick={handleImageButtonClick}>메인 이미지 업로드</Button>
<input
onChange={(e) => handleImageInputChange(e.target.files)}
type="file"
ref={imageInputRef}
style={{ display: 'none' }}
/>
<Button onClick={() => navigate('/')}>뒤로가기</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default WritePostPage;
import { useState, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { fetchPosts, deletePost, updatePost } from '../../redux/slices/postSlice';
import { supabase } from '../../service/supabase';
import {
Wrapper,
Container,
Header,
Title,
// Subtitle,
Form,
Label,
Input,
StyledReactQuill,
ButtonContainer,
Button
} from './EditPostPage.styles';
const EditPostPage = () => {
const { id } = useParams();
const postId = parseInt(id, 10);
const dispatch = useDispatch();
const navigate = useNavigate();
const post = useSelector((state) => state.posts.posts.find((post) => post.id === postId));
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [imageURL, setImageURL] = useState('');
const [previewImageURL, setPreviewImageURL] = useState(null);
const quillRef = useRef(null);
const imageInputRef = useRef(null);
const handleUpdatePost = () => {
if (title && content) {
dispatch(updatePost({ id: postId, title, content, imageURL: imageURL }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
const handleDeletePost = () => {
const confirmDelete = confirm('정말 이 글을 삭제하시겠습니까?');
if (confirmDelete) {
dispatch(deletePost(postId));
navigate('/');
}
};
async function handleImageInputChange(files) {
const [file] = files;
if (!file) {
setPreviewImageURL(null);
return;
}
const previewURL = URL.createObjectURL(file);
setPreviewImageURL(previewURL);
const { data } = await supabase.storage.from('avatars').upload(`avatar_${Date.now()}.png`, file);
const imageURL = `https://supabase.com/dashboard/project/dkodekduyiphnphkezzv/storage/buckets/avatars/${data.path}`;
setImageURL(imageURL);
}
const handleImageButtonClick = (e) => {
e.preventDefault();
imageInputRef.current.click();
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['underline', 'strike', 'blockquote', 'code-block'],
[{ color: [] }, { background: [] }],
['image']
]
};
useEffect(() => {
if (!post) {
dispatch(fetchPosts());
} else {
setTitle(post.title);
setContent(post.content);
}
}, [dispatch, post]);
return (
<Wrapper>
<Container>
<Header>
<Title>메인 이미지 미리보기</Title>
<br />
{previewImageURL && <img src={previewImageURL} alt="Preview" style={{ maxWidth: '200px' }} />}
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleUpdatePost}>수정</Button>
<Button onClick={handleImageButtonClick}>메인 이미지 업로드</Button>
<input
onChange={(e) => handleImageInputChange(e.target.files)}
type="file"
ref={imageInputRef}
style={{ display: 'none' }}
/>
<Button onClick={handleDeletePost}>삭제</Button>
<Button onClick={() => navigate('/')}>돌아가기</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default EditPostPage;
게시글 관련 부분은 다듬기만 할 듯하여 다른 페이지를 마저 개선해야겠음
'프로젝트 > 미니 프로젝트' 카테고리의 다른 글
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.06 |
---|---|
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.06 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.03 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.01 |
SingleProject_리액트 가계부 RTK 개선 (0) | 2024.05.30 |