Last active
February 20, 2020 14:50
-
-
Save u88803494/c15b611b0d76c3f46ae43181acc7ef74 to your computer and use it in GitHub Desktop.
串 API 跟同步畫面,還有實作偵測功能
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.form__datatype { | |
display: flex; | |
justify-content: space-between; | |
} | |
.form__empty { | |
color: red; | |
} | |
.form__empty--submit { | |
display: flex; | |
justify-content: flex-end; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect } from 'react'; | |
import { Button, Modal, Form } from 'react-bootstrap'; | |
import './editing_window.css'; | |
import * as webAPI from '../WebAPI'; | |
const EditingWindow = ({ onHide, show, post, status, handleChangePosts }) => { | |
const newPost = { title: '', author: '', body: '', }; | |
const defaultEmpty = { title: false, author: false, body: false, }; | |
const defaultSubmitType = { canSubmit: true, status: '', }; | |
const [thisPost, setThisPost] = useState(post ? post : newPost); | |
const [isEmpty, setEmpty] = useState(defaultEmpty); // 為了一開始不偵測 | |
const [submitType, setSubmitType] = useState(defaultSubmitType); | |
// 一開始先不偵測,因為本身載入的都是有內容的。 | |
const changePost = (e) => { | |
if (!e.target.value) { // 輸入時確認是否為空 | |
setEmpty({ ...isEmpty, [e.target.name]: true, }) | |
} else { | |
setEmpty({ ...isEmpty, [e.target.name]: false, }) | |
} | |
setThisPost({ ...thisPost, [e.target.name]: e.target.value, }) | |
} | |
const handleSubmit = () => { | |
if (!thisPost.title || !thisPost.author || !thisPost.body) { | |
setSubmitType({ canSubmit: false, status: '資料不全,無法送出,繼續完成資料才可送出', }); | |
return; | |
} | |
const whichAPI = (thisPost, status) => status === 'create' ? | |
webAPI.createPost(thisPost) : webAPI.updatePost(thisPost) | |
const submitPost = (status, thisPost) => { | |
handleChangePosts(status, thisPost); // 改變畫面上的資料 | |
onHide(); | |
} | |
const onError = (err) => { | |
setSubmitType({ canSubmit: false, status: `發生問題無法送出 ${err}`, }); | |
} | |
whichAPI(thisPost, status) | |
.then(res => res.status <= 300 && submitPost(status, thisPost)) | |
.catch(err => onError(err)) | |
} | |
useEffect(() => { | |
if (thisPost.title && thisPost.author && thisPost.body) { | |
setSubmitType({ canSubmit: true, status: '', }); | |
} // render 檢測值是否為空 | |
}, [thisPost]) | |
return ( | |
<Modal | |
size="lg" | |
aria-labelledby="contained-modal-title-vcenter" | |
centered | |
{...{ onHide, show }} | |
> | |
<Modal.Header closeButton> | |
<Modal.Title id="contained-modal-title-vcenter"> | |
{status === "editing" ? "你正在編輯文章" : "你正在新增文章"} | |
</Modal.Title> | |
</Modal.Header> | |
<Form> | |
<Modal.Body> | |
<Form.Group> | |
<div className="form__datatype"> | |
<Form.Label>標題</Form.Label> | |
<Form.Text className="form__empty"> | |
{isEmpty.title && '標題不能為空'} | |
</Form.Text> | |
</div> | |
<Form.Control | |
name="title" | |
type="text" | |
placeholder="Enter title" | |
value={thisPost && thisPost.title} | |
onChange={changePost} | |
/> | |
</Form.Group> | |
<Form.Group> | |
<div className="form__datatype"> | |
<Form.Label>作者</Form.Label> | |
<Form.Text className="form__empty"> | |
{isEmpty.author && '作者不能為空'} | |
</Form.Text> | |
</div> | |
<Form.Control | |
name="author" | |
type="text" | |
placeholder="author/作者" | |
value={thisPost && thisPost.author} | |
onChange={changePost} | |
/> | |
</Form.Group> | |
<Form.Group> | |
<div className="form__datatype"> | |
<Form.Label>內文</Form.Label> | |
<Form.Text className="form__empty"> | |
{isEmpty.body && '內容不能為空'} | |
</Form.Text> | |
</div> | |
<Form.Control | |
name="body" | |
as="textarea" | |
rows="5" | |
placeholder="輸入內文" | |
value={thisPost && thisPost.body} | |
onChange={changePost} | |
/> | |
</Form.Group> | |
<Form.Text className="form__empty form__empty--submit"> | |
{submitType.status} | |
</Form.Text> | |
</Modal.Body> | |
<Modal.Footer> | |
<Button | |
variant="outline-secondary" | |
onClick={onHide} | |
> | |
Close | |
</Button> | |
<Button | |
variant="outline-primary" | |
onClick={handleSubmit} | |
disabled={!submitType.canSubmit} | |
> | |
{status === 'editing' ? '儲存文章' : '新增文章'} | |
</Button> | |
</Modal.Footer> | |
</Form> | |
</Modal> | |
); | |
} | |
const DeleteWindow = ({ onHide, show, post, status, handleChangePosts }) => { | |
const [loadingState, setLoadingState] = useState('是的,我要刪除'); | |
useEffect(() => { | |
const finalExecution = (success) => { // 根據成功與否改變按鈕的內容 | |
success ? setLoadingState('刪除成功!') : setLoadingState('刪除失敗!') | |
setTimeout(() => { | |
success ? handleChangePosts(status, post) : setLoadingState('是的,我要刪除') | |
}, 1000) | |
} /** 放內部就不用使用 useCallback */ | |
if (loadingState === '刪除中........') { | |
webAPI.deletePost(post.id) // 改變伺服器 | |
.then(res => res.status < 300 && finalExecution(true) /* 改變父狀態 */) | |
.catch(() => finalExecution(false)) | |
} | |
}, [loadingState, handleChangePosts, post, status]); | |
const handleDelete = () => { | |
setLoadingState('刪除中........') | |
} | |
return ( | |
<Modal | |
size="lg" | |
aria-labelledby="contained-modal-title-vcenter" | |
centered | |
{...{ onHide, show }} | |
> | |
<Modal.Header closeButton> | |
<Modal.Title id="contained-modal-title-vcenter"> | |
警告!你正在刪除文章 | |
</Modal.Title> | |
</Modal.Header> | |
<Modal.Body> | |
你確定要刪除文章嗎? | |
</Modal.Body> | |
<Modal.Footer> | |
<Button variant="outline-secondary" onClick={onHide}> | |
不了,我不要刪除 | |
</Button> | |
<Button | |
variant="outline-danger" | |
onClick={handleDelete} | |
disabled={loadingState !== '是的,我要刪除'} | |
> | |
{loadingState} | |
</Button> | |
</Modal.Footer> | |
</Modal> | |
); | |
} | |
export { EditingWindow, DeleteWindow }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component, useState } from 'react'; | |
import { withRouter } from 'react-router-dom'; | |
import { ListGroup, Button, Spinner } from 'react-bootstrap'; | |
import './post_list.css'; | |
import { EditingWindow, DeleteWindow } from '../editing_window/'; | |
import { getPosts } from '../WebAPI'; | |
const ControllerButton = ({ post, handleChangePosts }) => { | |
const [editingShow, setEditingShow] = useState(false); | |
const [deleteShow, setDeleteShow] = useState(false); | |
const handleEdit = () => setEditingShow(true); // 另外寫出來省資源 | |
const handleDelete = () => setDeleteShow(true); | |
return ( | |
<div className="blog__controller"> | |
<Button variant="outline-success" onClick={handleEdit} >編輯</Button> | |
{ | |
editingShow && | |
<EditingWindow /** 編輯視窗 */ | |
show={editingShow} | |
onHide={() => setEditingShow(false)} | |
status="editing" | |
post={post} | |
handleChangePosts={handleChangePosts} | |
/> | |
} | |
<Button variant="outline-danger" onClick={handleDelete}>刪除</Button> | |
{ | |
deleteShow && | |
<DeleteWindow | |
show={deleteShow} | |
onHide={() => setDeleteShow(false)} | |
status="delete" | |
post={post} | |
handleChangePosts={handleChangePosts} | |
/> | |
} | |
</div> | |
); | |
}; | |
const RenderPosts = ({ data, history, handleChangePosts }) => ( | |
<> | |
{ | |
data.map(post => ( | |
<ListGroup.Item | |
key={post.id} | |
className="blog__post" | |
> | |
<div | |
className="blog__title" | |
onClick={() => history.push("/posts/" + post.id)} | |
> | |
{post.title} | |
</div> | |
<ControllerButton handleChangePosts={handleChangePosts} post={post} /> | |
</ListGroup.Item> | |
)) | |
} | |
</> | |
) | |
class Posts extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
data: [], | |
isCreate: false, | |
} | |
this.id = 1; | |
} | |
handleCreate = (isCreate) => { | |
this.setState({ isCreate, }) | |
} | |
handleChangePosts = (method, changeData) => { | |
/** 第一個變數是方式,第二個變更的資料 */ | |
const { data } = this.state; | |
switch (method) { | |
case 'create': | |
this.setState({ | |
data: [{ | |
...changeData, | |
createdAt: new Date().getTime(), // 取得當前的 timestamp,雖然應該會跟伺服器上的不同 | |
id: this.id, | |
}, | |
...data, // 放後面才能符合逆排序 | |
], | |
}) | |
this.id += 1; | |
break; | |
case 'editing': | |
this.setState({ | |
data: data.map((post) => { | |
if (post.id !== changeData.id) return post; | |
return { | |
...post, | |
...changeData, | |
}; | |
}) | |
}); | |
break; | |
case 'delete': | |
this.setState({ | |
data: data.filter(post => post.id !== changeData.id) | |
}) | |
break; | |
default: | |
console.log('一定是搞錯了什麼'); | |
} | |
} | |
componentDidMount() { | |
getPosts() // call api 也許可以改在 RenderPosts 那裡 | |
.then(res => { | |
this.setState({ | |
data: res.data | |
.filter(({ title, author, body }) => title && author && body) | |
.sort((a, b) => b.id - a.id), | |
}); // 太多無用資料,決定先篩選之後逆排序 | |
this.id = res.data.length !== 0 ? res.data[res.data.length - 1].id + 1 : 1; | |
}); | |
} | |
render() { /** 之後可以改成兩種呈現方式,條列式格狀顯示 */ | |
const { data, isCreate } = this.state; | |
const { history } = this.props; | |
return ( | |
<div className="blog"> | |
<header className="header"> | |
<div className="header__title">部落格文章</div> | |
<div className="header__newpost"> | |
<Button | |
variant="outline-primary" | |
onClick={() => this.handleCreate(true)} | |
> | |
新增文章 | |
</Button> | |
{ | |
isCreate && | |
<EditingWindow /* 新增共用編輯視窗 */ | |
show={isCreate} | |
onHide={() => this.handleCreate(false)} | |
status="create" | |
handleChangePosts={this.handleChangePosts} | |
/> | |
} | |
</div> | |
</header> | |
<main className="blog__posts"> | |
{/** 判斷是否讀取中 */ | |
data.length ? | |
<RenderPosts | |
data={data} | |
history={history} | |
handleChangePosts={this.handleChangePosts} | |
/> : | |
<Spinner animation="border" /> | |
} | |
</main> | |
</div> | |
) | |
} | |
} | |
export default withRouter(Posts); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment