Notice
Recent Posts
Recent Comments
Link
Hello It's good to be back ^_^
[ECC-프론트엔드 2팀] 17주차 스터디 본문
교재: 소플의 처음 만난 리액트 14 ~ 16
공부한 페이지: pp. 390 ~ 513
실습한 내용: https://github.com/HongYeonLee/ECC-Frontend-Study
GitHub - HongYeonLee/ECC-Frontend-Study: ECC 49기 FW 프론트엔드 2팀 스터디 내용을 기록하는 레포지토리입
ECC 49기 FW 프론트엔드 2팀 스터디 내용을 기록하는 레포지토리입니다. Contribute to HongYeonLee/ECC-Frontend-Study development by creating an account on GitHub.
github.com
목차
14 컨텍스트
- 14.1 컨텍스트란 무엇인가?
- 14.2 언제 컨텍스트를 사용해야 할까?
- 14.3 컨텍스트를 사용하기 전에 고려할 점
- 14.4 컨텍스트 API
- 14.5 여러 개의 컨텍스트 사용하기
- 14.6 useContext
- 14.7 컨텍스트를 사용하여 테마 변경 기능 만들기
15 스타일링
- 15. 1 CSS
- 15.2 styled-components
- 15.3 styled-components를 사용하여 스타일링해 보기
16 미니 프로젝트-미니 블로그 만들기
실습 - 컨텍스트를 사용하여 테마 변경 기능 만들기
코드
ThemeContext.jsx
import React from "react";
const ThemeContext = React.createContext();
ThemeContext.displayName = "ThemeContext";
export default ThemeContext;
MainContent.jsx
import { useContext } from "react";
import ThemeContext from "./ThemeContext";
function MainContent(props){
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style = {{
width: "100vw",
height: "100vh",
padding: "1.5rem",
backgroundColor: theme == "light" ? "white" : "black",
color: theme == "light" ? "black" : "white",
}}
>
<p>안녕하세요, 테마 변경이 가능한 웹사이트 입니다.</p>
<button onClick = {toggleTheme}>테마 변경</button>
</div>
);
}
export default MainContent;
DarkOrLight.jsx
import { useState, useCallback } from "react";
import ThemeContext from "./ThemeContext";
import MainContent from "./MainContent";
function DarkOrLight(props){
const [theme, setTheme] = useState("light");
const toggleTheme = useCallback(() => {
if (theme == "light"){
setTheme("dark");
}
else if (theme == "dark"){
setTheme("light");
}
}, [theme]);
return (
<ThemeContext.Provider value = {{ theme, toggleTheme }}>
<MainContent />
</ThemeContext.Provider>
);
}
export default DarkOrLight;
실행화면


실습 - styled-components를 사용하여 스타일링해 보기
코드
import styled from "styled-components";
const Wrapper = styled.div`
padding: 1rem;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
background-color: lightgrey;
`;
const Block = styled.div`
padding: ${(props) => props.padding};
border: 1px solid black;
border-radius: 1rem;
background-color: ${(props) => props.backgroundColor};
color: white;
font-size: 2rem;
font-weight: bold;
text-align: center;
`;
const blockItems = [
{
label: "1",
padding: "1rem",
backgroundColor: "red",
},
{
label: "2",
padding: "3rem",
backgroundColor: "green",
},
{
label: "3",
padding: "2rem",
backgroundColor: "blue",
},
];
function Blocks(props){
return (
<Wrapper>
{blockItems.map((blockItem) => {
return(
<Block
padding = {blockItem.padding}
backgroundColor = {blockItem.backgroundColor}
>
{blockItem.label}
</Block>
);
})}
</Wrapper>
);
}
export default Blocks;
실행내용
코드
Button.jsx
import React from "react";
import styled from "styled-components";
const StyledButton = styled.button`
padding: 8px 16px;
font-size: 16px;
border_width: 1px;
obrder-radius: 8px;
cursor: pointer;
`;
function Button(props){
const { title, onClick } = props;
return <StyledButton onClick={onClick}>{title || "button"}</StyledButton>;
}
export default Button;
TextInput.jsx
import React from "react";
import styled from "styled-components";
const StyledTextarea = styled.textarea`
width: calc(100% - 32px);
${(props) =>
props.height &&
`
height: ${props.height}px;
`}
padding: 16px;
font-size: 16px;
line-height: 20px;
`;
function TextInput(props){
const { height, value, onChange } = props;
return <StyledTextarea height = {height} value = {value} onChange = {onChange}></StyledTextarea>;
}
export default TextInput;
PostList.jsx
import React from "react";
import styled from "styled-components";
import PostListItem from "./PostListItem";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
:not(:last-child){
margin-bottom: 16px;
}
`;
function PostList(props){
const { posts, onClickItem } = props;
return (
<Wrapper>
{posts.map((post, index) => {
return(
<PostListItem
key = {post.id}
post = {post}
onClick = {() => {
onClickItem(post);
}}
></PostListItem>
);
})}
</Wrapper>
);
}
export default PostList;
PostListItem.jsx
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 16px;
display: flex;
flex-direction: column;
aligh-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgrey;
}
`;
const TitleText = styled.p`
font-size: 20px;
font-weight: 500;
`;
function PostListItem(props){
const { post, onClick } = props;
return (
<Wrapper onClick={onClick}>
<TitleText>{post.title}</TitleText>
</Wrapper>
);
}
export default PostListItem;
CommentList.jsx
import React from "react";
import styled from "styled-components";
import CommentListItem from "./CommentListItem";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
:not(:last-child){
margin-bottom: 16px;
}
`;
function CommentList(props){
const { comments } = props;
return (
<Wrapper>
{comments.map((comment, index) => {
return (
<CommentListItem
key = {comment.id}
comment = {comment}
>
</CommentListItem>
);
})}
</Wrapper>
);
}
export default CommentList;
CommentListItem.jsx
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 8px 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgrey;
}
`;
const ContentText = styled.p`
font-size: 16px;
white-space: pre-wrap;
`;
function CommentListItem(props){
const { comment } = props;
return(
<Wrapper>
<ContentText>
{comment.content}
</ContentText>
</Wrapper>
);
}
export default CommentListItem;
MainPage.jsx
import React from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import PostList from "../list/PostList";
import Button from "../ui/Button";
import data from "../../data.json";
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
:not(:last-child){
margin-bottom: 16px;
}
`;
function MainPage(props){
const {} = props;
const navigate = useNavigate();
return (
<Wrapper>
<Container>
<Button
title = "글 작성하기"
onClick = {() => {
navigate("/post-write");
}}
>
</Button>
<PostList
posts = {data}
onClickItem = {(item) => {
navigate(`/post/${item.id}`);
}}
>
</PostList>
</Container>
</Wrapper>
)
}
export default MainPage;
PostViewPage.jsx
import React, { useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import styled from "styled-components";
import CommentList from "../list/CommentList";
import TextInput from "../ui/TextInput";
import Button from "../ui/Button";
import data from "../../data.json";
const Wrapper = styled.div`
padding: 16px;
width: calc(100% -32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
:not(:last-child){
margin-bottom: 16px;
}
`;
const PostContainer = styled.div`
padding: 8px 16px;
border: 1px solid grey;
border-radius: 8px;
`;
const TitleText = styled.p`
font-size: 28px;
font-weight: 500;
`;
const ContentText = styled.p`
font-size: 20px;
line-height: 32px;
white-space: pre-wrap;
`;
const CommentLabel = styled.p`
font-size: 16px;
font-weight: 500;
`;
function PostViewPage(props){
const navigate = useNavigate();
const { postId } = useParams();
const post = data.find((item) => {
return item.id == postId;
});
const [comment, setComment] = useState("");
return (
<Wrapper>
<Container>
<Button
title = "뒤로 가기"
onClick = {() => {
navigate("/");
}}
>
</Button>
<PostContainer>
<TitleText>
{post.title}
</TitleText>
<ContentText>
{post.content}
</ContentText>
</PostContainer>
<CommentLabel>댓글</CommentLabel>
<CommentList comments = {post.comments}></CommentList>
<TextInput
height = {40}
value = {comment}
onChange = {(event) => {
setComment(event.target.value);
}}
></TextInput>
<Button
title = "댓글 작성하기"
onClick = {() => {
navigate("/");
}}
>
</Button>
</Container>
</Wrapper>
);
}
export default PostViewPage;
PostWritePage.jsx
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import PostList from "../list/PostList";
import TextInput from "../ui/TextInput";
import Button from "../ui/Button";
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
:not(:last-child) {
margin-bottom: 16px;
}
`;
function PostWritePage(props){
const navigate = useNavigate();
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
return (
<Wrapper>
<Container>
<TextInput
height = {20}
value = {title}
onChange ={(event) => {
setTitle(event.target.value);
}}
></TextInput>
<TextInput
height = {480}
value = {content}
onChange ={(event) => {
setContent(event.target.value);
}}
></TextInput>
<Button
title = "글 작성하기"
onClick = {() => {
navigate("/");
}}
>
</Button>
</Container>
</Wrapper>
);
}
export default PostWritePage;
App.js
import React from "react";
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import styled from "styled-components";
//Pages
import MainPage from "./component/page/MainPage";
import PostWritePage from "./component/page/PostWritePage";
import PostViewPage from "./component/page/PostViewPage";
const MainTitleText = styled.p`
font-size: 24px;
font-weight: bold;
text-align: center;
`;
function App(props) {
return (
<BrowserRouter>
<MainTitleText>홍연의 미니 블로그</MainTitleText>
<Routes>
<Route index element = {<MainPage></MainPage>}></Route>
<Route path = "post-write" element = {<PostWritePage></PostWritePage>}></Route>
<Route path = "post/:postId" element = {<PostViewPage></PostViewPage>}></Route>
</Routes>
</BrowserRouter>
);
}
export default App;
실행내용
'Study > 프론트엔드' 카테고리의 다른 글
[ECC-프론트엔드 2팀] 17주차 스터디 (0) | 2025.01.25 |
---|---|
[ECC-프론트엔드 2팀] 16주차 스터디 (0) | 2025.01.11 |
[ECC-프론트엔드 2팀] 15주차 스터디 (0) | 2025.01.04 |
[ECC-프론트엔드 2팀] 14주차 스터디 (2) | 2024.12.28 |
[ECC-프론트엔드 2팀] 13주차 스터디 (1) | 2024.12.21 |