//TemperatureInput.jsx
import React,{useState} from "react";
const scalesNames = {
c: "섭씨",
f: "화씨",
}
function TemperatureInput(props){
// input 태그의 값이 변경 이벤트 발생 시 동작할 함수, event 객체를 매개변수로 가져온다
const handleChange = (event) => {
// 부모 컴포넌트에서 전달받은 props 중 이름이 onTemperatureChange인 함수를 실행한다
// onTemperatureChange함수는 부모 컴포넌트의 state 값을 수정하는 함수
// 자식 컴포넌트의 값이 부모 컴포넌트로 전달이 된다
props.onTemperatureChange(event.target.value);
};
return(
<fieldset>
<legend>
{/*온도*/}
온도를 입력해주세요 (단위 : {scalesNames[props.scale]})
</legend>
{/*단위*/}
{/* onChange는 input 태그의 값 변경 시 발생하는 이벤트 중 하나.
해당 이벤트에 handleChange라는 함수를 연동시켰다. */}
<input value={props.temperature} onChange={handleChange}/>
</fieldset>
);
}
export default TemperatureInput;
// Culculator.jsx
import React,{useState} from "react";
import TemperatureInput from "./TemperatureInput";
function BoilingVerdict(props){ //props를 통해 온도가 전달되었다.
if (props.celsius >= 100) {
return <p>물이 끓습니다</p>
}
return <p>물이 끓지 않습니다.</p>
}
// 매개변수로 화씨 온도를 받아서 섭씨 온도로 변환
function toCelsius(fahrenheit) {
return ((fahrenheit - 32) * 5 ) / 9;
}
// 매개변수로 섭씨 온도를 받아서 화씨 온도로 변환
function toFahrenheit(celsius){
return (celsius * 9) / 5 + 32;
}
// 매개변수로 현재 온도와 온도 변환을 위한 함수를 받아서 사용
function tryConvert(temperature, convert){
const input = parseInt(temperature);
// 들어온 값이 숫자가 아니면 빈 배열로 변환한다. => true라고 뜨면 숫자가 아니다
//입력받은 온도가 숫자인지 아닌지 확인, 숫자가 아니면 빈 문자열 반환한다
if(Number.isNaN(input)) {
return "";
}
// 두 번째 매개변수로 받은 변환 함수를 사용하여 표시 온도를 변환
const output = convert(input);
// 수학 관련 함수를 사용하여 소수점 이하 부분 처리 (반올림)
const rounded = Math.round(output * 1000)/1000;
return rounded.toString();
}
function Culculator(props){
// state로 설정
const [temperature, setTemperature] = useState('');
const [scale, setScale] = useState('c');
const handleCelsiuChange = (temperature) => {
setTemperature(temperature);
setScale('c');
}
const handleFahrenheitChange = (temperature) => {
setTemperature(temperature);
setScale('f');
}
// 현재 온도를 표시 방식에 따라서 변환
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature; //섭씨
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature; //화씨
return(
<div>
{/* 자식 컴포넌트 호출 시 props에 2개의 데이터와 state의 값을 변경할 수 있는 함수를 제공*/}
<TemperatureInput scale={'c'} temperature={celsius} onTemperatureChange={handleCelsiuChange}/>
<TemperatureInput scale={'f'} temperature = {fahrenheit} onTemperatureChange = {handleFahrenheitChange}/>
{/*온도를 props를 통해서 전달. 실수로 표현하기 위해서 parseFloat()를 사용*/}
<BoilingVerdict celsius={parseFloat(celsius)} />
{/* parseFloat: 문자 받은 것을 소수점으로 바꿔준다 */}
</div>
);
}
export default Culculator;
Toolbar.jsx, ThemedButton.jsx, App.jsx
//App.jsx
// 컨텍스트 예제
import React from "react";
import Toolbar from "./Toolbar";
// 컨텍스트 : 자식 컴포넌트에 데이터를 한번에 전달할 수 있는 객체
// React.createContext : 컨텍스트 객체를 생성
// Provider를 사용하여 생성된 컨텍스트를 사용할 컴포넌트를 지정
// 사용법 :
// const 컨텍스트명 = React.createContext(기본값);
// Context.Provider : 생성된 컨텍스트를 사용할 자식 컴포넌트를 지정 시 사용
// value 속성에 전달하고 하는 데이터를 지정, 해당 데이터를 사용하고자 하는 자손 컴포넌트를 감싸면
// 어느 위치의 자손 컴포넌트이든 상관없이 해당 데이터에 접근이 가능함.
// 여러 개를 사용하는 것이 가능하다. (provider와 consumer 둘 다 일대다 다대일 다 가능하다.)
// 사용법 :
// <컨텍스트명.Provider value = {전달할 데이터}>
// <자손 컴포넌트/> 얼마나 깊은지 상관없이 사용가능
// </컨텍스트명.Provider>
// Context.Consumer : Provider로 감싸진 자손 컴포넌트 중 컨텍스트에 저장된 데이터를 사용하고자 할 경우
// Consumer를 사용하여 저장된 데이터를 가져옴
// 여러 개를 사용하는 것이 가능하다.
// 함수 컴포넌트에서 사용
// 사용법 :
// <컨텍스트명.Consumer>
// {value => <컴포넌트 key={value}/>}
// </컨텍스트명.Consumer>
// Class.contextType : 클래스 컴포넌트에 저장된 내용을 저장할 경우 사용
// 사용법 :
// componentDidMount() {
// let value = this.context;
// }
// 사용하고자 하는 컴포넌트 클래스.contextType = 생성된 컨텍스트명;
// Context.displayName : 컨텍스트의 이름을 지정할 때 사용
// 개발자 도구에서 displayName으로 지정한 이름이 출력된다.
// 지정한 이름.Provider, 지정한 이름.Consumer
// 사용법 :
// 컨텍스트명.displayName = '사용하고자 하는 이름';
// useContext : 컨텍스트를 사용하기 위한 react 훅
// 사용법 :
// const value = useContext(컨텍스트명);
function App(props) {
// 자식 컴포넌트에 theme라는 이름으로 dark라는 데이터를 전달 -> toolbar
return <Toolbar theme={"dark"}/>
}
export default App;
// Toolbar.jsx
// 컨텍스트 예제
import React from "react";
import ThemedButton from "./ThemedButton";
function Toolbar(props){
return(
<div>
{/*자식 컴포넌트로 theme라는 이름의 데이터를 전달*/}
<ThemedButton theme={props.theme}/>
</div>
)
}
export default Toolbar;
//ThemedButton.jsx
// 컨텍스트
import React from "react";
const styles = {
bg : {
backgroundColor : "white"
}
}
// 최종위치
function Button(props){
// 부모에게서 전달받은 theme를 사용하고 있음
if (props.theme === "dark"){
styles.bg.backgroundColor = "black";
}
else {
styles.bg.backgroundColor = "white";
}
return <button style={styles.bg}>테마 적용 버튼</button>
}
function ThemedButton(props) {
// 자식 컴포넌트로 theme로 전달
return <Button theme = {props.theme}/>
}
export default ThemedButton;
App2.jsx 컨텍스트를 사용하지 안으면 데이터를 아래로 아래로 가져가야 하는데, 컨텍스트를 이용하면 중간에 붕 떠서 데이터를 필요한 곳에 바로 보낼 수 있다.
App에는
이렇게 다른 이름으로 데이터 보내는 것 가능
function Toolbar2(props){
return (
<ThemeContext.Consumer>
{value => <ThemedButton2 aaa={value}/>}
</ThemeContext.Consumer>
// {/*<div>*/}
// {/* <ThemedButton2/>*/}
// {/*</div>*/}
);
}
여기에 저 코드 추가했다.
두 단계, 세 단계 정도는 일반 쓰는 것을 추천.
MainContents.jsx, ThemeContext.jsx, DarkOrLight.jsx
//ThemeContext.jsx
import React from "react";
// 컨텍스트 객체 생성, 기본값으로 'light'를 설정
const ThemeContext = React.createContext('light');
// 컨텍스트 이름 설정
ThemeContext.displayName = "ThemeContext";
// 컨텍스트만 만들고 변수만 export 해줬다. 함수아니다.
// 컨텍스트 객체를 외부에서 사용할 수 있도록 export
export default ThemeContext;
// 필요한 곳에서 어디든지 로딩해서 사용할 수 있다.
// 컨텍스트을 위한 컨텍스트용 파일이다.
//MainContents.jsx
import React, {useContext} from "react";
// 파일로 생성된 컨텍스트를 import하여 사용, 여러 컴포넌트에서 활용이 가능하다.
import ThemeContext from "./ThemeContext";
function MainContents(props){
//useContext() 훅을 사용하여 <Context.Consumer> 없이 컨텍스트에 저장된 정보를 사용함.
const {theme, toggleTheme} = useContext(ThemeContext); // 객체로 받기 때문에 {} 사용
return(
<div style={{
width : "100vw",
height: "100hw",
padding : "1.5rem", //rem: 고정값, em: 부모의 배율에 따라 변동
backgroundColor : theme == "light" ? "white" : "black",
color: theme == "light" ? "black" : "white"}}
>
<p>테마 변경이 가능한 웹 사이트</p>
{/* button 태그의 클릭 이벤트에 컨텍스트를 통해 가져온 함수를 설정함 */}
<button onClick={toggleTheme}>테마변경</button>
</div>
)
}
export default MainContents;
// DarkOrLight.jsx
import React,{useState,useCallback} from "react";
// 파일로 생성된 컨텍스트를 import하여 사용, 여러 컴포넌트에서 활용이 가능하다.
import ThemeContext from "./ThemeContext"; //컨텍스트 불러와서 사용
import MainContents from "./MainContents";
function DarkOrLight(props){
// state 로 등록.
// 부모의 state값을 수정할 수 있는 함수를 지정.
const [theme, setTheme] = useState('light');
//useCallback() : 콜백함수를 반환하는 리액트 훅, 메모이제이션 기능을 가지고 있다.
// 메모이제이션 기능을 사용할 수 있어 지정한 의존성 배열에 등록된 데이터가 변경되었을 경우에만 동작한다.
// useEffect()를 사용하면 변경되지 않고 동일하더라도 재랜더링이 된다. -> 시간이 오래 걸림, 리소스 많이 사용
// 하지만 useCallback()을 사용하면 리소스도 아끼고 속도도 더 빠르다.
// 여기서는 theme의 값이 변경이 될 때만 재랜더링 시킨다.
const toggleTheme = useCallback(()=>{
//state로 지정된 값이 변경될 경우에만 동작
if (theme == "light") {
setTheme("dark");
}
else if (theme == "dark") {
setTheme("light");
}
}, [theme])
return (
// context Provider로 해당 컴포넌트 아래의 자손 컴포넌트에서 지정한 데이터를 사용할 수 있도록 한다.
<ThemeContext.Provider value={{theme, toggleTheme}}>
<MainContents/>
</ThemeContext.Provider>
)
}
export default DarkOrLight;
folder4/App3.jsx
App4.jsx
import React, {useContext} from "react";
const ThemeContext = React.createContext('light');
ThemeContext.displayName = 'ThemeContext';
const UserContext = React.createContext({
name : 'Guest',
});
UserContext.displayName = "UserContext";
function Layout(props){
return (
<div>
<SideBar/>
<Content/>
</div>
);
}
function SideBar(props) {
return <h2>사이드 바</h2>
}
function Content(props){
return(
<div>
<Profile/>
</div>
)
}
function Profile(props){
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return(
<div>
<h3>user : {user.name}</h3>
<h3>theme : {theme}</h3>
</div>
)
}
function App4(){
const {signedInUser, theme} = { signedInUser : {name : "아이유"}, theme: 'dark'};
return (
<ThemeContext.Provider value={theme}>
{/*<ThemeContext.Provider value={"blue"} 이거랑 동일> */}
<UserContext.Provider value={signedInUser}>
{/*<UserContext.Provider value={{name : "아이유"}}>이거랑 동일 */}
<Layout/>
</UserContext.Provider>
</ThemeContext.Provider>
)
}
export default App4;
https://reactrouter.com/en/main
이 두 개를 많이 사용한다.
터미널에 이렇게 쳐라
PS C:\java505\intellij\react\java505_react_test3> npm install react-router-dom
다 설치되면 프로젝트 재시작 한 번 해주기
리액트 라우터 돔
Layout.jsx Home.jsx Blogs.jsx Contact.jsx NoPage.jsx
리스트를 누르면 주소 뒤에 /(값)이 되고 그 페이지에 값이 보인다.
//App7.jsx
import React from "react";
import Culculator from "./folder4/Culculator";
import App from "./folder4/App";
import App2 from "./folder4/App2";
import App4 from "./folder4/App4";
import DarkOrLight from "./folder4/DarkOrLight";
// 라우터 추가된 후 추가된 부분
import {BrowserRouter,Routes, Route} from "react-router-dom";
import Layout from "./Pages/Layout";
import Home from "./Pages/Home";
import Blogs from "./Pages/Blogs";
import Contact from "./Pages/Contact";
import NoPage from "./Pages/NoPage";
function App7() {
return (
// <div>
// <Culculator />
// <App/>
// <hr/>
// <App2/>
// <hr />
// <DarkOrLight/>
// <App4/>
// </div>
// 리액트 라우터 : spa방식의 리액트 앱을 mpa방식으로 사용할 수 있도록 해주는 라이브러리
// Routes : 페이지 객체를 여러 개 가질 수 있는 객체
// Route : 화면에 그려지는 페이지 객체
// Path : 웹 브라우저에 표시되는 url 주소 설정, 절대 경로/상대경로 다 사용 가능,
// * 사용시 모든 페이지를 뜻함.(path url을 정확하게 입력해야 접속이 가능하다.)
// element : path로 지정된 url 주소와 매칭되는 컴포넌트 (localhost : 3030)
// <Link> : 리액트 라우터에서 사용되는 링크 전용 컴포넌트, html의 <a>태그와 같다.
// to : url 주소 입력, Route 컴포넌트에 path로 설정된 주소
// <Outlet> : 현재 선택된 경로의 컴포넌트를 화면에 표시
<BrowserRouter>
<Routes>
<Route path={"/"} element={<Layout/>}>
<Route index element={<Home/>}/>
<Route path={"blogs"} element={<Blogs />}/>
<Route path={"contact"} element={<Contact />}/>
<Route path={"*"} element={<NoPage />}/>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App7;
//Layout.jsx
import React from "react";
import {Outlet, Link} from "react-router-dom";
function Layout(props){
return (
<div>
<nav>
<ul>
<li>
<Link to = {"/"}>Home</Link>
</li>
<li>
<Link to={"/blogs"}>Blogs</Link>
</li>
<li>
<Link to = {"/contact" }>Contact</Link>
</li>
</ul>
</nav>
<Outlet/>
</div>
);
}
export default Layout;
//Home.jsx
import React from "react";
function Home(props) {
return <h2>Home 페이지</h2>;
}
export default Home;
//Blogs.jsx
import React from "react";
function Blogs(props) {
return <h2>Blogs 페이지</h2>
}
export default Blogs;
// Contact.jsx
import React from "react";
function Contact(props){
return <h2>Contact 페이지</h2>
}
export default Contact;
// NoPage.jsx
import React from "react";
function NoPage(props) {
return <h1>404 에러 페이지</h1>
}
export default NoPage;
문제1 ) 아래의 주소의 웹 페이지를 리액트를 사용하여 컴포넌트로 구성하고, 해당 페이지의 네비게이션 메뉴 클릭 시 다른 페이지의 내용이 출력되도록 react-router-dom을 사용하여 각각의 페이지를 표시하는 프로그램을 작성하세요
1. 메뉴 구성 : main, board, about
누르면 화면 나오게 만들어라아아아아