TIL 2023-01-06 컨텍스트, 라우터 돔

JYR00·2023년 1월 6일
0

TIL

목록 보기
56/60

App7.jsx, foler4 생성



TemperatureInput.jsx

//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

// 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

//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

// 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

//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에는


Provider, Consumer 여러 개 사용가능 예제


이렇게 다른 이름으로 데이터 보내는 것 가능

Toolbar2.jsx

function Toolbar2(props){
    return (
        <ThemeContext.Consumer>
            {value => <ThemedButton2 aaa={value}/>}
        </ThemeContext.Consumer>

    // {/*<div>*/}
    // {/*    <ThemedButton2/>*/}
    // {/*</div>*/}
    );
}

여기에 저 코드 추가했다.

두 단계, 세 단계 정도는 일반 쓰는 것을 추천.

테마변경 버튼과 검은색 바탕

MainContents.jsx, ThemeContext.jsx, DarkOrLight.jsx

ThemeContext.jsx

//ThemeContext.jsx

import React from "react";

// 컨텍스트 객체 생성, 기본값으로 'light'를 설정
const ThemeContext = React.createContext('light');
// 컨텍스트 이름 설정
ThemeContext.displayName = "ThemeContext";
// 컨텍스트만 만들고 변수만 export 해줬다. 함수아니다.

// 컨텍스트 객체를 외부에서 사용할 수 있도록 export
export default ThemeContext;

// 필요한 곳에서 어디든지 로딩해서 사용할 수 있다.
// 컨텍스트을 위한 컨텍스트용 파일이다.

MainContents.jsx

//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

// 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;

컨텍스트 2개 이상 사용하기

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;

react router 설치해야 한다

https://reactrouter.com/en/main

이 두 개를 많이 사용한다.

터미널에 이렇게 쳐라

PS C:\java505\intellij\react\java505_react_test3> npm install react-router-dom

다 설치되면 프로젝트 재시작 한 번 해주기

리액트 라우터 돔

pages 폴더생성

Layout.jsx Home.jsx Blogs.jsx Contact.jsx NoPage.jsx



리스트를 누르면 주소 뒤에 /(값)이 되고 그 페이지에 값이 보인다.

App7.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

//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

//Home.jsx

import React from "react";

function Home(props) {
    return <h2>Home 페이지</h2>;

}

export default Home;

Blogs.jsx

//Blogs.jsx

import React from "react";

function Blogs(props) {
    return <h2>Blogs 페이지</h2>
}

export default Blogs;

Contact.jsx

// Contact.jsx

import React from "react";
function Contact(props){
    return <h2>Contact 페이지</h2>
}

export default Contact;

NoPage.jsx

// NoPage.jsx

import React from "react";

function NoPage(props) {
    return <h1>404 에러 페이지</h1>

}

export default NoPage;

문제1 ) 아래의 주소의 웹 페이지를 리액트를 사용하여 컴포넌트로 구성하고, 해당 페이지의 네비게이션 메뉴 클릭 시 다른 페이지의 내용이 출력되도록 react-router-dom을 사용하여 각각의 페이지를 표시하는 프로그램을 작성하세요
1. 메뉴 구성 : main, board, about
누르면 화면 나오게 만들어라아아아아
























0개의 댓글