본문 바로가기

Frontend/프로젝트로 배우는 React.js

섹션 6. 블로그 수정 페이지

40 Edit 페이지 route 추가

routes.js

    {
        path: '/blogs/:id/edit',
        component: EditPage
    },

로 수정. 무엇을 edit하는 것인지 알 수 있게

 

ShowPage.js

import { useParams } from "react-router";
import axios from 'axios';
import { useEffect, useState } from "react";
import LoadingSpinner from "../components/LoadingSpinner";
import { Link } from "react-router-dom"; //추가

const ShowPage = () => {
    const { id } = useParams();
    const [post, setPost] = useState(null);
    const [loading, setLoading] = useState(true); 
    
    const getPost = (id) => { 
        axios.get(`http://localhost:3001/posts/${id}`).then((res) => {
            setPost(res.data);
            setLoading(false);
        })
    };

    useEffect(() => {
        getPost(id);
    }, [id]);

    const printDate = (timestamp) => {
        return new Date(timestamp).toLocaleString();
    }

    if (loading) {
        return <LoadingSpinner />
    }
    
    return ( 
        <div>
            <div className="d-flex"> {/*추가*/}
                <h1 className="flex-grow-1">{post.title}</h1>
                <div>
                    <Link 
                        className="btn btn-primary"
                        to={`/blogs/${id}/edit`}
                    >
                        Edit
                    </Link>
                </div>
            </div>
            <small className="text-muted">
                Created At: {printDate(post.createdAt)}
            </small>
            <hr />
            <p>{post.body}</p>
        </div>
    );
}

export default ShowPage;

41 수정 페이지 폼 1

Create할 때 썼던 BlogForm 재사용

 

EditPage.js

import BlogForm from "../components/BlogForm";

const EditPage = () => {
    return (
        <div>
            <BlogForm editing={true} />
        </div>
    );
};

export default EditPage;

 

BlogForm.js

import { useEffect, useState } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router';
import { useParams } from 'react-router-dom/cjs/react-router-dom.min';

const BlogForm = ({ editing }) => {  //editing일 경우에는
    const history = useHistory();
    const { id } = useParams(); //추가

    const [title, setTitle] = useState('');
    const [body, setBody] = useState('');

    useEffect(() => {  //추가
        axios.get(`http://localhost:3001/posts/${id}`).then(res => {
            setTitle(res.data.title);
            setBody(res.data.body);
        })
    }, [id]);

    const onSubmit = () => {
        axios.post('http://localhost:3001/posts', {
            title: title,
            body: body,
            createdAt: Date.now()
        }).then(() => {
            history.push('/blogs');
        })
    };

    return (
        <div>
            <h1>{editing ? 'Edit' : 'Create'} a blog post</h1> {/*변경*/}
            <div className="mb-3">
            <label className="form-label">Title</label>
            <input 
                className="form-control" 
                value={title}
                onChange={ (e) => {
                setTitle(e.target.value);  
                } }
            />
        </div>

        <div className="mb-3">
            <label class Name="form-label">Body</label>
            <textarea 
                className="form-control" 
                value={body}
                onChange={ (e) => {
                    setBody(e.target.value);
                } }
                rows="10"  //추가
            />
        </div>

        <button 
            className="btn btn-primary"
            onClick={onSubmit}
        >
        {editing ? 'Edit' : 'Post'} {/*변경*/}
        </button>

        </div>
    );
};

BlogForm.propTypes = { //edit
    editing: Boolean
}

BlogForm.defaultProps = {  //create
    editing: false
}

export default BlogForm;

 


42 수정 페이지 폼 2

button을 누르면 onSubmit함수 공유해서 실행해서 db에 post 생성..

-> 이 안에서도 condition을 넣어서 editing일 경우에는 ~

 

업데이트할 때

 

const onSubmit = () => {
        if (editing) {
            axios.patch(`http://localhost:3001/posts/${id}`, {
                title: title,
                body: body,
            }).then(res => { //response가 오면
                console.log(res);
            })
        } else {
            axios.post('http://localhost:3001/posts', {
                title: title,
                body: body,
                createdAt: Date.now()
            }).then(() => {
                history.push('/blogs');
            })
        }
    };

 

-> 수정이 잘 된다!


43 수정 페이지 폼 3

title이랑 body 안 바꾸면 버튼 비활성화

Edit 완료하면 이전 페이지로 이동

 

BlogForm.js

import { useEffect, useState } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router';
import { useParams } from 'react-router-dom/cjs/react-router-dom.min';

const BlogForm = ({ editing }) => {
    const history = useHistory();
    const { id } = useParams();

    const [title, setTitle] = useState('');
    const [originalTitle, setOriginalTitle] = useState('');  //추가

    const [body, setBody] = useState('');
    const [originalBody, setOriginalBody] = useState('');  //추가

    useEffect(() => {
        if (editing) {  //빼먹은거
            axios.get(`http://localhost:3001/posts/${id}`).then(res => {
                setTitle(res.data.title);
                setOriginalTitle(res.data.title);
                setBody(res.data.body);
                setOriginalBody(res.data.body);
            })
        }
    }, [id, editing]);

    const isEdited = () => {
        return title !== originalTitle || body !== originalBody; //수정이 된 거
    }

    const onSubmit = () => {
        if (editing) {
            axios.patch(`http://localhost:3001/posts/${id}`, {
                title: title,
                body: body,
            }).then(res => {
                console.log(res);
                history.push(`/blogs/${id}`)  //추가
            })
        } else {
            axios.post('http://localhost:3001/posts', {
                title: title,
                body: body,
                createdAt: Date.now()
            }).then(() => {
                history.push('/blogs');
            })
        }
    };

    return (
        <div>
            <h1>{editing ? 'Edit' : 'Create'} a blog post</h1>
            <div className="mb-3">
            <label className="form-label">Title</label>
            <input 
                className="form-control" 
                value={title}
                onChange={ (e) => {
                setTitle(e.target.value);  
                } }
            />
        </div>

        <div className="mb-3">
            <label class Name="form-label">Body</label>
            <textarea 
                className="form-control" 
                value={body}
                onChange={ (e) => {
                    setBody(e.target.value);
                } }
                rows="10"
            />
        </div>

        <button 
            className="btn btn-primary"
            onClick={onSubmit}
            disabled={editing && !isEdited()} //추가
        >
        {editing ? 'Edit' : 'Post'}
        </button>

        </div>
    );
};

BlogForm.propTypes = {
    editing: Boolean
}

BlogForm.defaultProps = {
    editing: false
}

export default BlogForm;

 

=> originalTitle.........질문 ★


44 수정 페이지 폼 4

edit 버튼 옆 cancel 버튼

 

BlogForm.js

import { useEffect, useState } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router';
import { useParams } from 'react-router-dom/cjs/react-router-dom.min';

const BlogForm = ({ editing }) => {
    const history = useHistory();
    const { id } = useParams();

    const [title, setTitle] = useState('');
    const [originalTitle, setOriginalTitle] = useState('');

    const [body, setBody] = useState('');
    const [originalBody, setOriginalBody] = useState('');

    useEffect(() => {
        if (editing) {
            axios.get(`http://localhost:3001/posts/${id}`).then(res => {
                setTitle(res.data.title);
                setOriginalTitle(res.data.title);
                setBody(res.data.body);
                setOriginalBody(res.data.body);
            })
        }
    }, [id, editing]);

    const isEdited = () => {
        return title !== originalTitle || body !== originalBody;
    };

    const goBack = () => {  //추가
        if (editing) {
            history.push(`/blogs/${id}`);
        } else {  //create인 경우 -> 그냥 리스트로 이동
            history.push('/blogs');
        }
    };

    const onSubmit = () => {
        if (editing) {
            axios.patch(`http://localhost:3001/posts/${id}`, {
                title: title,
                body: body,
            }).then(res => {
                console.log(res);
                history.push(`/blogs/${id}`)
            })
        } else {
            axios.post('http://localhost:3001/posts', {
                title: title,
                body: body,
                createdAt: Date.now()
            }).then(() => {
                history.push('/blogs');
            })
        }
    };

    return (
        <div>
            <h1>{editing ? 'Edit' : 'Create'} a blog post</h1>
            <div className="mb-3">
            <label className="form-label">Title</label>
            <input 
                className="form-control" 
                value={title}
                onChange={ (e) => {
                setTitle(e.target.value);  
                } }
            />
        </div>

        <div className="mb-3">
            <label class Name="form-label">Body</label>
            <textarea 
                className="form-control" 
                value={body}
                onChange={ (e) => {
                    setBody(e.target.value);
                } }
                rows="10"
            />
        </div>

        <button 
            className="btn btn-primary"
            onClick={onSubmit}
            disabled={editing && !isEdited()}
        >
            {editing ? 'Edit' : 'Post'}
        </button>
        <button  //추가
            className="btn btn-danger ms-2"
            onClick={goBack}
        >
            Cancel
        </button>

        </div>
    );
};

BlogForm.propTypes = {
    editing: Boolean
}

BlogForm.defaultProps = {
    editing: false
}

export default BlogForm;

45 수정 페이지 폼 5

공개여부 체크박스

 

BlogForm.js

import { useEffect, useState } from 'react';
import axios from 'axios';
import { useHistory } from 'react-router';
import { useParams } from 'react-router-dom/cjs/react-router-dom.min';

const BlogForm = ({ editing }) => {
    const history = useHistory();
    const { id } = useParams();

    const [title, setTitle] = useState('');
    const [originalTitle, setOriginalTitle] = useState('');

    const [body, setBody] = useState('');
    const [originalBody, setOriginalBody] = useState('');

    const [publish, setPublish] = useState(false); //추가
    const [originalPublish, setOriginalPublish] = useState(false); //추가

    useEffect(() => {
        if (editing) {
            axios.get(`http://localhost:3001/posts/${id}`).then(res => {
                setTitle(res.data.title);
                setOriginalTitle(res.data.title);
                setBody(res.data.body);
                setOriginalBody(res.data.body);
                setPublish(res.data.publish);
                setOriginalPublish(res.data.publish);
            })
        }
    }, [id, editing]);

    const isEdited = () => {
        return title !== originalTitle 
        || body !== originalBody
        || publish !== originalPublish;  //축
    };

    const goBack = () => {
        if (editing) {
            history.push(`/blogs/${id}`);
        } else {
            history.push('/blogs');
        }
    };

    const onSubmit = () => {
        if (editing) {
            axios.patch(`http://localhost:3001/posts/${id}`, {
                title: title,
                body: body,
                publish: publish
            }).then(res => {
                console.log(res);
                history.push(`/blogs/${id}`)
            })
        } else {
            axios.post('http://localhost:3001/posts', {
                title: title,
                body: body,
                publish: publish,
                createdAt: Date.now()
            }).then(() => {
                history.push('/blogs');
            })
        }
    };

    const onChangePublish = (e) => {  //추가
        console.log(e.target.checked);
        setPublish(e.target.checked);
    };

    return (
        <div>
            <h1>{editing ? 'Edit' : 'Create'} a blog post</h1>
            <div className="mb-3">
            <label className="form-label">Title</label>
            <input 
                className="form-control" 
                value={title}
                onChange={ (e) => {
                setTitle(e.target.value);  
                } }
            />
        </div>

        <div className="mb-3">
            <label class Name="form-label">Body</label>
            <textarea 
                className="form-control" 
                value={body}
                onChange={ (e) => {
                    setBody(e.target.value);
                } }
                rows="10"
            />
        </div>
        <div className="form-check mb-3"> {/*추가*/}
            <input
                className="form-check-input"
                type="checkbox"
                checked = {publish}
                onChange={onChangePublish}
            />
            <label className="form-check-label">
                Publish
            </label>
        </div>

        <button 
            className="btn btn-primary"
            onClick={onSubmit}
            disabled={editing && !isEdited()}
        >
            {editing ? 'Edit' : 'Post'}
        </button>
        <button
            className="btn btn-danger ms-2"
            onClick={goBack}
        >
            Cancel
        </button>

        </div>
    );
};

BlogForm.propTypes = {
    editing: Boolean
}

BlogForm.defaultProps = {
    editing: false
}

export default BlogForm;