一、先整理一下資料夾吧!
1. 重點:
a. SRC 資料夾底下的 index.js 檔案,不可以被放在任何資料夾底下
b. SRC 資料夾底下的 index.js 檔案,先 import App from "./components/App";
c. App 資料夾底下的 index.js 裡面可以寫成
import App from "./App";
export default App;
或者比較新的語法 re-export 可以用 export { default } from "./App";
d. 等於是 SRC 資料夾底下的 index.js 檔案去找到 -> components/App 再去找到 -> App 資料夾裡面的 index.js -> App.js
e. 這是常用的小撇步
2. 目前的結構是:
第一層 SRC: 內含 components 資料夾、constants 資料夾、.eslintrc.json 檔案、index.css 檔案、index.js 檔案
第二層 components 資料夾,內含三個資料夾,分別是:
a. App: 裡面有三個檔案,App.js、App test.js、index.js
b. Message Board
c. Todo: 之前做的 todo list
第二層 constants 裡面有 breakpoint.js 檔案
二、先來切個版吧!
1.犯的錯誤:
a. css 寫錯 component,畫面就跑不出來了,下次要邊寫邊確認畫面
2. 切版內容
// App.js code
import React from "react";
import styled from "styled-components";
const Page = styled.div`
width: 300px;
margin: 0 auto;
`;
const Title = styled.h1`
color: blue;
`;
const MessageForm = styled.form`
margin-top: 16px;
`;
const MessageTextArea = styled.textarea`
display: block;
width: 100%;
`;
const SubmitButton = styled.button`
margin-top: 8px;
`;
const MessageList = styled.div`
margin-top: 16px;
`;
const MessageContainer = styled.div`
border: 1px solid black;
padding: 8px 16px;
border-radius: 5px;
`;
const MessageHead = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`;
const MessageAuthor = styled.div`
color: blue;
font-size: 14px;
`;
const MessageTime = styled.div``;
const MessageBody = styled.div`
margin-top: 16px;
font-size: 16px;
`;
function Message({ author, time, children }) {
return (
<MessageContainer>
<MessageHead>
<MessageAuthor>{author}</MessageAuthor>
<MessageTime>{time}</MessageTime>
</MessageHead>
<MessageBody>{children}</MessageBody>
</MessageContainer>
);
}
function App() {
return (
<Page>
<Title>留言板</Title>
<MessageForm>
<MessageTextArea rows={10} />
<SubmitButton>Submit</SubmitButton>
</MessageForm>
<MessageList>
<Message author={"christy"} time={"2021-11-11 11:11:11"}></Message>
</MessageList>
</Page>
);
}
export default App;
三、來拿資料吧!把 API 串上去
1. 串完 API 內容
先抓資料 -> 處理錯誤 -> 修 ESlint 提示:PropTypes
// App.js code
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
const API_ENDPOINT = "https://student-json-api.lidemy.me/comments";
const Page = styled.div`
width: 360px;
margin: 0 auto;
`;
const Title = styled.h1`
color: blue;
`;
const MessageForm = styled.form`
margin-top: 16px;
`;
const MessageTextArea = styled.textarea`
display: block;
width: 100%;
`;
const SubmitButton = styled.button`
margin-top: 8px;
`;
const MessageList = styled.div`
margin-top: 16px;
`;
const MessageContainer = styled.div`
border: 1px solid black;
padding: 8px 16px;
border-radius: 5px;
& + & {
margin-top: 8px;
}
`;
const MessageHead = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 3px;
border-bottom: 3px solid white;
`;
const MessageAuthor = styled.div`
color: blue;
font-size: 14px;
`;
const MessageTime = styled.div``;
const MessageBody = styled.div`
margin-top: 16px;
font-size: 16px;
`;
const ErrorMessage = styled.div`
margin-top: 16px;
color: red;
`;
function Message({ author, time, children }) {
return (
<MessageContainer>
<MessageHead>
<MessageAuthor>{author}</MessageAuthor>
<MessageTime>{time}</MessageTime>
</MessageHead>
<MessageBody>{children}</MessageBody>
</MessageContainer>
);
}
Message.propTypes = {
author: PropTypes.string,
time: PropTypes.string,
children: PropTypes.node,
};
function App() {
const [messages, setMessages] = useState(null);
const [apiError, setApiError] = useState(null);
useEffect(() => {
fetch(API_ENDPOINT)
.then((res) => res.json())
.then((data) => {
setMessages(data);
})
.catch((err) => {
setApiError(err.message);
});
}, []);
return (
<Page>
<Title>留言板</Title>
<MessageForm>
<MessageTextArea rows={10} />
<SubmitButton>Submit</SubmitButton>
</MessageForm>
{apiError && (
<ErrorMessage>Something went wrong. {apiError.toString()}</ErrorMessage>
)}
{messages && messages.length === 0 && <div>No Message</div>}
<MessageList>
{messages &&
messages.map((message) => (
<Message
key={message.id}
author={message.nickname}
time={new Date(message.createdAt).toLocaleString()}
>
{message.body}
</Message>
))}
</MessageList>
</Page>
);
}
export default App;
2. 實作新增留言功能
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
const API_ENDPOINT =
"https://student-json-api.lidemy.me/comments?_sort=createdAt&_order=desc";
const Page = styled.div`
width: 360px;
margin: 0 auto;
`;
const Title = styled.h1`
color: blue;
`;
const MessageForm = styled.form`
margin-top: 16px;
`;
const MessageTextArea = styled.textarea`
display: block;
width: 100%;
`;
const SubmitButton = styled.button`
margin-top: 8px;
`;
const MessageList = styled.div`
margin-top: 16px;
`;
const MessageContainer = styled.div`
border: 1px solid black;
padding: 8px 16px;
border-radius: 5px;
& + & {
margin-top: 8px;
}
`;
const MessageHead = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 3px;
border-bottom: 3px solid white;
`;
const MessageAuthor = styled.div`
color: blue;
font-size: 14px;
`;
const MessageTime = styled.div``;
const MessageBody = styled.div`
margin-top: 16px;
font-size: 16px;
`;
const ErrorMessage = styled.div`
margin-top: 16px;
color: red;
`;
const Loading = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
color: white;
display: flex;
font-size: 30px;
align-items: center;
justify-content: center;
`;
function Message({ author, time, children }) {
return (
<MessageContainer>
<MessageHead>
<MessageAuthor>{author}</MessageAuthor>
<MessageTime>{time}</MessageTime>
</MessageHead>
<MessageBody>{children}</MessageBody>
</MessageContainer>
);
}
Message.propTypes = {
author: PropTypes.string,
time: PropTypes.string,
children: PropTypes.node,
};
function App() {
const [messages, setMessages] = useState(null);
const [messageApiError, setMessageApiError] = useState(null);
const [value, setValue] = useState();
const [postMessageError, setPostMessageError] = useState();
const [isLoadingPostMessage, setIsLoadingPostMessage] = useState(false);
const fetchMsgs = () => {
return fetch(API_ENDPOINT)
.then((res) => res.json())
.then((data) => {
setMessages(data);
})
.catch((err) => {
setMessageApiError(err.message);
});
};
const handleTextareaChange = (e) => {
setValue(e.target.value);
};
const handleTextareaFocus = () => {
setPostMessageError(null);
};
const handleFormSubmit = (e) => {
e.preventDefault();
setIsLoadingPostMessage(true);
fetch("https://student-json-api.lidemy.me/comments", {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
nickname: "hi",
body: value,
}),
})
.then((res) => res.json())
.then((data) => {
setIsLoadingPostMessage(false);
if (data.ok === 0) {
setPostMessageError(data.message);
return;
}
setValue("");
fetchMsgs();
})
.catch((err) => {
setIsLoadingPostMessage(false);
setPostMessageError(err.message);
});
};
useEffect(() => {
fetchMsgs();
}, []);
return (
<Page>
{isLoadingPostMessage && <Loading>Loading...</Loading>}
<Title>留言板</Title>
<MessageForm onSubmit={handleFormSubmit}>
<MessageTextArea
value={value}
onChange={handleTextareaChange}
onFocus={handleTextareaFocus}
rows={10}
/>
<SubmitButton>Submit</SubmitButton>
{postMessageError && <ErrorMessage>{postMessageError}</ErrorMessage>}
</MessageForm>
{messageApiError && (
<ErrorMessage>
Something went wrong. {messageApiError.toString()}
</ErrorMessage>
)}
{messages && messages.length === 0 && <div>No Message</div>}
<MessageList>
{messages &&
messages.map((message) => (
<Message
key={message.id}
author={message.nickname}
time={new Date(message.createdAt).toLocaleString()}
>
{message.body}
</Message>
))}
</MessageList>
</Page>
);
}
export default App;