TIL 2023-01-05 React , 조건부 렌더링, 상속, 데이터 주고받기

JYR00·2023년 1월 5일
0

TIL

목록 보기
55/60

오늘도 홧팅

깃허브

App6, folder3생성

오늘 총평 - 열심히 달렸따아아..

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App2';
import reportWebVitals from './reportWebVitals';
import App2 from "./App2";
import App3 from "./App3";
import App33 from "./App33";
import App5 from "./App5";
import App6 from "./App6";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(

    // strict mode : 자바스크립트의 문법을 강력하게 확인한다는 의미. 자바스크립트는 간소화된 문법이다. 그렇지만 이게 있으면 좀 더 강하게 확인한다
    // React.StrictMode : 배포 버전에는 제외되고 개발 버전에서는 동작하는 엄격모드, 몇 가지 함수를 중복 실행 하여 잘못된 것이 없는지 개발자에게
    // 확인하도록 한다. 다 주석 처리해도 잘 실행된다
    // <React.StrictMode>

    //{/*/!*     strict 모드란 react 앱 내의 잠재적인 문제를 알아내기 위한 도구라고 나와있습니다.*!/,*/}
    //{/*/!*<App />*!/*/}
    //{/*/!*    <App2/>*!/*/}
    //{/*/!*    <App3/>*!/*/}
    //{/*/!*    <App33/>*!/*/}
    //{/*    */}

    //{/*/!*</React.StrictMode>*!/*/}
// <App5/>
    <App6/>

);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App6.jsx

import React from "react";
import Goal from "./folder3/Goal";
import Greeting from "./folder3/Greeting";
import LoginControl from "./folder3/LoginControl";
import MailBox from "./folder3/MailBox";
import Counter from "./folder3/Counter";
import MainPage from "./folder3/MainPage";
import LandingPage from "./folder3/LandingPage";
import AttendanceBook from "./folder3/AttendanceBook";
import NameForm from "./folder3/NameForm";
import RequestForm from "./folder3/RequestForm";
import FruitSelect from "./folder3/FruitSelect";
import Reservation from "./folder3/Reservation";
import Child from "./folder3/Child";
import Parents from "./folder3/Parents";
import Parents2 from "./folder3/Parents2";

function App6() {
    return(
        <div className={"container"}>
            <Goal isGoal = {false}/>
            <hr />
            <Greeting isLoggedIn = {false}/>
            <hr />
            <LoginControl/><hr />
            <MailBox unreadMessages={50}/><hr />
            <Counter/><hr />
            <MainPage/><br /><hr />
            <LandingPage/><hr />
            <AttendanceBook/><hr />
            <NameForm/>
            <RequestForm/>
            <FruitSelect/>
            <Reservation/>
            <Parents/>
            <br/>
            <Parents2/>

            <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
        </div>
    )

}
export default App6;

조건부 렌더링

Goal.jsx, MadeGoal.jsx, MissedGoal.jsx 파일 생성



Goal.jsx

//Goal.jsx

import React from "react";
import MadeGoal from "./MadeGoal";
import MissedGoal from "./MissedGoal";

// 조건부 렌더링 : 어떠한 조건에 따라서 화면에 표시할 렌더링이 달라지는 것.
// if문 사용 시 render() 함수가 동작하는 return 부분에서 조건문을 사용하는 것이 아니라 return 밖에서 조건문을 사용하고
// 안에서는 삼항 연산자를 사용한다.

// 자바스크립트의 true / false
// true : 논리형 true, 빈 오브젝트 타입 {}, 빈 배열 [], 0이 아닌 숫자, 빈 문자열이 아닌 문자열
// false : 논리형 false, 숫자 0 혹은 0.0, 빈 문자열 '', ``(배틱), null, undefined, NaN(Not A Number),  

function Goal(props){
    const isGoal = props.isGoal;

    if (isGoal){ // if문은 렌더링 바깥에서 해야 한다. 렌더링 안에서 하고 싶다면 삼항연산자 사용해야한다
        return <MadeGoal/>
    }

    return <MissedGoal/>
}

export default Goal;

MadeGoal.jsx

//MadeGoal.jsx

import React from "react";

function MadeGoal(){

        return<h1>Goal</h1>

}

export default MadeGoal;

MissedGoal.js

// MissedGoal.js

import React from "react";

function MissedGoal(){
    return <h2>Missed!!</h2>
}
export default MissedGoal;




조건부 렌더링 예제2 - LoginControll.jsx,Greeting.jsx,




로그인 버튼 누르면 다시 로그아웃버튼으로 바꾸며 글이 바뀐다

LoginControl.jsx

// LoginControl.jsx

import React, {useState} from "react";
import Greeting from "./Greeting";



function LoginButton (props) {
    return (
        <button type={"button"} onClick={props.onClick}>로그인</button>
    );
}

function LogoutButton (props) {
    return (
        <button type={"button"} onClick={props.onClick}>로그아웃</button>
    );
}


function LoginControl(props){
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    // 자바스크립트는 변수에 함수도 저장가능하다.
    const handleLoginClick = () => {
        setIsLoggedIn(true);
    }
    const handleLogoutClick = () => {
        setIsLoggedIn(false);
    }

    // 엘리먼트 변수 : 자바스크립트 변수에 리액트 컴포넌트를 저장한 것
    // 조건부 렌더링을 사용하기 위해서 자바스크립트 변수 button에 react 컴포넌트를 저장함
    let button;
    
    // state의 상태에 따라서 element 변수에 저장될 리액트 컴포넌트를 변경
    if (isLoggedIn) {
        button = <LogoutButton onClick = {handleLogoutClick}/>;
    }
    else {
        button = <LoginButton onClick = {handleLoginClick}/>;
    }

    return (
        <div>
            <Greeting isLoggedIn = {isLoggedIn}/>
            {/*엘리먼트 변수에 저장된 리액트 컴포넌트가 출력된다*/}
            {button} {/*출력되는 부분. 위의 if문에 따라 모양이 달라진다.*/}
        </div>
    );
}


export default LoginControl;

Greeting.jsx

//Greeting.jsx

import React from "react";

function Greeting(props){
    const isLoggedIn = props.isLoggedIn;

    if (isLoggedIn) {
        return <UserGreeting/>
    }

    return <GuestGreeting/>
}

// 이렇게 써도 된다
// function Greeting(props){
//     const isLoggedIn = props;
// }

function UserGreeting(){
    return <h2>다시 오셨군요!!!!!</h2>
}

function GuestGreeting(){
    return <h2>회원가입을 해 주세요!!</h2>
}

export default Greeting;

남은 메세지 + 카운트

Mailbox.jsx Counter.jsx

&& 연산자에서 앞의 조건이 false면 뒷 부분은 아예 나오지 않는다.



app6에서 데이터를 주지 않아도 0이 출력된다.

MailBox.jsx

//MailBox.jsx

import React from "react";

function MailBox(props){
    const unreadMessages = props.unreadMessages;

    //inline if : jsx 문법으로 렌더링을 진행하는 render()함수 내부에서 if 문을 사용할 수 없기 때문에
    // '&&'를 사용하거나 삼항 연산자를 고려하여 조건부 렌더링을 진행할 수 있음

    // && 연산자 사용시 && 오른쪽에 있는 피연산자는 왼쪽에 있는 피연산자의 값에 따라 렌더링이 결정된다.
    // && 연산자 왼쪽의 피연산자 값이 false일 경우 오른쪽의 피연산자는 연산 평가 자체가 동작하지 않는다.
    // && 연산자 왼쪽의 피연산자는 연산 평가가 이루어지기 때문에 해당 값이 그대로 출력이 된다.
        return(
            <div>
                <h1>안녕하세요</h1>
                <h3>inline if (&& 연산자 테스트)</h3>
                {
                    unreadMessages> 0 &&
                    <h2>
                        현재 {unreadMessages}개의 읽지 않은 메세지가 있습니다.
                    </h2>
                }
                {/*{*/}
                {/*    unreadMessages.length> 0 &&*/}
                {/*    <h2>*/}
                {/*        현재 {unreadMessages.length}개의 읽지 않은 메세지가 있습니다.*/}
                {/*    </h2>*/}
                {/*원래 이랬는데, .length는 문자열만 인식하기 때문에 인식하지 못했었다*/}
                {/*}*/}
            </div>
        );
}

export default MailBox;

Counter.jsx

// Counter.jsx

import React from "react";


function Counter(props) {
    const count = 0;
    //값 자체가 0 이라서 fasle다. 그렇지만 && 앞 쪽의 값이 출력된다. 화면에 0이라고 뜬다.
    return(
        <div>
            {/*앞의 조건 false다. 데이터를 넣지 않았으니까. 그러니까 위의 0만 출력하고 끝난다.*/}
            {count && <h1>현재 카운트 : {count}</h1>}
        </div>
    );
}

export default Counter;




삼항 연산자로 해보았다.

		{
                isLoggedIn ? <LogoutButton onClick={handleLogoutClick}/> : <LoginButton onClick = {handleLoginClick}/>
            }

UserStatus.jsx

// UserStatus.jsx

import React from "react";

function UserStatus (props){
    return (
        <div>
            이 사용자는 현재 <b>{props.isLoggedIn? '로그인' : '로그인하지 않은 '}</b> 상태입니다.
        </div>
    )
}

export default UserStatus;

문제 1) 아래의 소스에서 삼항 연산자를 사용한 부분을 if ~else 문을 사용하여 조건부 렌더링으로 수정하세요

UserStatus.jsx

// UserStatus.jsx

import React from "react";

function UserStatus (props){

    // 문제 1) 아래의 소스에서 삼항 연산자를 사용한 부분을 if ~else 문을 사용하여 조건부 렌더링으로 수정하세요
    //loginControl.jsx에 있다.

    //만들던 즁..
    // if (props.isLoggedIn = false){
    //     return ("로그인 하지 않은 상태입니다.")
    // }
    // else {
    //     return ("로그인한 상태입니다.")
    // }

    const flag = props.isLoggedIn;
    if(flag){
        return <p>사용자는 현재 <b>로그인</b>상태입니다.</p>
    }
    else {
        return <p>사용자는 현재 <b>비로그인</b>상태입니다.</p>
    }

    // return (
    //     <div>
            {/*이 사용자는 현재 <b>{props.isLoggedIn? '로그인' : '로그인하지 않은 '}</b> 상태입니다.*/}

        // </div>
    // )
}

export default UserStatus;

버튼 누르면 값에 따라 배너 보이게


WarningBanner.jsx

// WarningBanner.jsx

import React from "react";

// 컴포넌트 렌더링 막기
// 리액트에서 렌더링을 하고 싶지 않을 경우 null을 사용한다.

function WarningBanner({warning}) {
    // if (!{warning}){
    //     return null;
    // }
    // return <div>경고!!</div>;
    if (!warning){
        return null
    }
    return <div>경고!!</div>;

}
export default WarningBanner;

MainPage.jsx

// MainPage.jsx

import React, {useState} from "react";
import WarningBanner from "./WarningBanner";

function MainPage(props){
    const [showWarning, setShowWarning] = useState(false);

    const handleToggleClick = () => {
        setShowWarning(prevState => !prevState);
    }

    return (
        <div>
            <WarningBanner warning={showWarning}/>
            <button onClick={handleToggleClick}>{showWarning ? '감추기' : '보이기'}</button>
        </div>
    )
}
export default MainPage;

상태에 따라 배너 + 버튼 변경

Toolbar.jsx

// Toolbar.jsx

import React from "react";

const styles = {
    wrapper : {
        padding : 16,
        display : 'flex',
        flexDirection : 'row',
        borderBottom : "1px solid grey",
    },

    greeting : {
        marginRight : 8,
    },
}

function Toolbar(props){
    // object 의 확장표현식으로 props 객체에 있는 데이터를 모두 받음
    const {isLoggedIn, onClickLogin, onClickLogOut} = props;

    return (
        <div style = {styles.wrapper}>
            {/* &&연산자로 isLoggedIn의 값에 따라 화면 렌더링 여부 결정*/}
            {isLoggedIn && <span style={styles.greeting}>환영합니다.</span>}

            {/*삼항 연산자로 로그인/로그아웃 버튼을 출력*/}
            {isLoggedIn
                ? (<button onClick={onClickLogOut}>로그아웃</button> )
                : (<button onClick={onClickLogin}>로그인</button>)
            }
        </div>
    );
}

export default Toolbar;

LandingPage.jsx

// LandingPage.jsx

import React,{useState} from "react";
import Toolbar from "./Toolbar";

function LandingPage(props){
    // isLoggedIn을 state로 설정
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    // 클릭 이벤트 시 사용할 함수를 생성
    // state 값 수정을 위한 함수 생성
    const onClickLogin = () => {
        setIsLoggedIn(true);
    }
    const onClickLogOut = () => {
        setIsLoggedIn(false);
    }
    return (
        <div>
            {/*{{onClickLogOut}에 들어있는건 위에 있는 함수이다. 위에서 받아와서 랜팅페이지에서 넘겨줘서stae값 변경해주는 걸 이 페이지에서
            대신 해주고 있음}*/}
            <Toolbar isLoggedIn={isLoggedIn} onClickLogin={onClickLogin} onClickLogOut={onClickLogOut}/>
            <div style={{padding : 16}}>베너 확인</div>
        {/*    {{padding : 16}} 바깥 {}는 데이터를 집어넣기 위해 안의 {}는 js의 오브젝트라는 걸 알려줌*/}
        </div>
    )
}

export default LandingPage;





Attendencebook.jsx

//AttendenceBook.jsx

import React from "react";

// 앞으로 리스트로 데이터를 넣거나 가져올 때 key 부분을 만들어라. 구분할 수 있는 유일한 값이면 된다.
const students = [
    {id : 1, name : "Inje"},
    {id : 2, name : "Steve"},
    {id : 3, name : "Bill"},
    {id : 4, name : "Jeff"},
];

// 리스트 : 비슷한 데이터를 모아둔 것
// key : 리액트에서 각 객체나 아이템을 구분할 수 있는 고유의 값. key가 없으면 서로 구분할 수 없다.
// react에서는 해당 리스트 사이에서 엘리먼트를 구분할 수 있는 고유한 값이면 된다.

// id를 사용, id라는 것 자체가 고유한 값이라는 의미이므로 key값으로 사용할 수 있다.

function AttendanceBook(){
    return(
        <ul>
            {students.map((student)=>{
                return <li>{student.name}</li>
            })}
        </ul>
    );
}

export default AttendanceBook;



이름과 요청사항 적는 폼



칸에 내용을 넣어 제출 버튼 누르면 alert로 그 내용이 뜬다.

NameForm.jsx

//NameForm.jsx
// 텍스트 박스를 이용한 이벤트

import React, {useState} from "react";

function NameForm(props){
    //변수 value를 state로 설정, 기본값은 빈 문자열.

    const [value, setValue] = useState(''); //기본값을 비웠다

    
    // event 발생 시 동작하는 함수
    // 매개변수로 자바스크립트의 event 객체를 가져온다.
    // 실제 이벤트가 발생한 태그의 정보가 담겨져 있다.(input 태그)
    
    const handleChange = (event) => {
        //state로 설정된 변수 value의 값을 변경하는 setValue() 함수 실행
        // 여기서 event는 자바스크립트 이벤트 객체다.
        // target => 이벤트를 발생시킨 대상 찾아온다
        // 객체를 통해서 이벤트가 발생한 대상에 대한 정보를 가져옴
        
        //이름을 치면 값이 변경되니까 이벤트가 발생해서 여기로 온다.
        // value : 이벤트가 발생하면서 생긴 값을 가져와라
        
        // 이벤트 발생 대상이 가지고 있는 value 값을 사용하여 state를 변경

        setValue(event.target.value);
    }
    
    // submit 버튼에 이벤트가 발생되면 실행하라
    const handleSubmit = (event) => {
        alert("입력한 이름 : " + value);
        event.preventDefault();
    }


    return(
        <form onSubmit={handleSubmit} className={"container"} action={"#"}>
            <div className={"my-3"}>
                <label for = {"user-name"} className={"form-label"}>이름 : </label>
                {/*input 태그 부분이 중요함*/}
                {/* input 태그의 value를 state로 설정된 변수를 사용하여 화면에 데이터를 출력 */}
                {/* 사용자 입력에 의해서 값이 변경될 경우 event를 발생한다 (onChange)*/}
                <input type={"text"} className = {"form-control"} value={value} onChange={handleChange}/>
            </div>
            <div className = {"my-3"}>
                <button type={'submit'} className={"btn btn-primary"}>제출</button>
            </div>
        </form>
    );
}
export default NameForm;

RequestForm.jsx

//RequestForm.jsx
// textarea 사용하기

import React, {useState} from "react";

function RequestForm(props) {
    const [value, setValue] = useState("요청 사항을 입력하세요")

    //이거 넣어준거밖에 없음
    const handleChange = (event) => {
        setValue(event.target.value);
    }

    const handleSubmit = (event) => {
        alert('입력한 요청사항 : ' + value);
        event.preventDefault(); //화면 재랜더링하는 것을 막아준다.
    }

    return(
        <form onSubmit={handleSubmit} className={"container"}>
            <div className={"my-3"}>
                {/*!!!!!!반드시!! onChange를 넣어줘야합니다!!! 이거 없으면 수정이 안됩니다!! 기존값만 계속 나타날거다.!!!!!*/}
                <label for={"user-contents"} className={"form-label"}>요청사항 : < / label >
                <textarea value={value} className={'form-control'} onChange={handleChange}/>
            </div>
            <div className={"my-3"}>
                <button type={"submit"} className = {"btn btn-primary"}>제출</button>
            </div>
        </form>
    )
}
export default RequestForm;

과일 선택하기



아래에 있는 건 다중선택이 가능하다.

FruitSelect.jsx

//FruitSelect.jsx

import React, {useState} from "react";

function FruitSelect(props){
    const [value, setValue] = useState('grape');
    const [value2, setValue2] = useState([]);

    const handleChange=(e)=>{
        setValue(e.target.value);
    }

    // 배열로 넣어주기
    const handleChange2=(e)=>{
        // select box 의 자식 태그인 option 태그를 모두 가져옴
        const options = e.target.options;
        // 선택된 데이터를 저장하기 위한 배열
        let items = [];

        // option의 크기 만큼 반복
        for (let i=0; i < options.length; i++){
            // option태그 중 선택된 태그인지 아닌지 확인
            if(options[i].selected){
                // 선택된 태그의 내용만 배열에 저장
                items.push(options[i].value);
            }
        }
        // setState() 함수를 통해서 state 업데이트
        setValue2(items);
    }

    const handleSubmit = (e) => {
        alert(`선택한 과일 : ${value} 다중선택한 과일 : ${value2}`);
        e.preventDefault();
    }

    return (
        <form onSubmit={handleSubmit}>
            <div className={"my-3"}>
                <label for={"sel1"} className={"form-label"}>과일을 선택하세요 : </label>
                {/*select 사용 시 기본 값 설정 value 속성으로 진행*/}
                {/*multiple 속성 사용 시 true/false 사용. html에서는 multiple만 썼었음*/}
                {/*리액트에서 기본 html의 속성 중 속성명으로만 효과가 발생하는 속성에 대해서도 값을 제어하기 위해서
                속성명 = {값} 형식으로 사용함 */}
                <select id={"sel1"} className={"form-select"} value={value} onChange={handleChange} disabled = {false}>
                    <option value={"apple"}>사과</option>
                    <option value={"banana"}>바나나</option>
                    <option value={"grape"}>포도</option>
                    <option value={"watermelon"}>수박</option>
                </select>

                <br/>

                <div className = {"my-3"}>
                    <label for = {"sel2" } className={"form-label"}>과일을 선택해주세요</label>
                    <select id={"sel2"} className={"form-select"} onChange={handleChange2} value={value2} multiple={true}>
                        <option value={"apple"}>사과</option>
                        <option value={"banana"}>바나나</option>
                        <option value={"grape"}>포도</option>
                        <option value={"watermelon"}>수박</option>
                    </select>
                </div>
            </div>
            <div className={"my-3"}>
                <button type={"submit"} className={"btn btn-primary"}>제출</button>
            </div>
        </form>
    )
}

export default FruitSelect;

아침식사여부





아침식사 여부 옆의 체크박스로 true / false 값이 출력된다.

Reservaton.jsx

// Reservaton.jsx

import React, {useState} from "react";

function Reservation(props) {
    const [haveBreakfast, setHaveBreakfast] = useState(true);
    const [numberOfGuest, setNumberOfGuest] = useState(2);

    const handleSubmit = (e) => {
        alert(`아침 식사 여부 : ${haveBreakfast}, 방문객 수 : ${numberOfGuest}`);
        e.preventDefault();
    }

    return(
        <form onSubmit={handleSubmit}>
            <div className={"my-3"}>
                <label className={"form-label"}>아침식사 여부 : </label>
                <input type={"checkbox"} checked={haveBreakfast} onChange={(e)=>{
                    setHaveBreakfast(e.target.checked);
                }}/>
            </div>
            <div className = {"my-3" }>
                <label className={"form-label"}>방문객 수 : </label>
                <input type={"number"} value={numberOfGuest} onChange={(e)=>{
                    setNumberOfGuest(e.target.value);
                }}/>
            </div>
            <div className={"my-3"}>
                <button type={"submit"} className={"btn btn-primary"}>제출</button>
            </div>
        </form>
    );
}

export default Reservation;              

데이터 상속 (Parents, Child)

Parents.jsx

// Parents.jsx
// 부모 컴포넌트
import React from "react";
import Child from "./Child";

function Parents(props){
    return(
        <div>
            <h1>Parents 컴포넌트 영역</h1>
            <Child value={"부모 데이터"}/>
        </div>
    )
}

export default Parents;

Child.jsx

// Child.jsx
// 자식 컴포넌트 Parents.jsx의 자식이다.

import React from "react";

function Child(props){
    return(
        <div>
            <h3>Child 컴포넌트 영역</h3>
            
            <p>전달받은 값 : {props.value}</p>
        </div>
    )
}

export default Child;




데이터 끌어올리기(Parents2, Child2)


Parents2.jsx

// Parents2.jsx
// 부모 컴포넌트
import React, {useState} from "react";
import Child2 from "./Child2";

function Parents2(props){
    // childData를 state로 설정
    const [childData, setChildData] = useState('');

    return(
        <div>
            <h1>Parents2 컴포넌트 영역</h1>
            {/*자식 컴포넌트 호출*/}
            {/*자식 컴포넌트 호출 시 value, childValue라는 키에 데이터를 저장해서 전달*/}
            {/* childValue 키에 setState() 함수를 저장해서 전달 */}
            <Child2 value={"부모 데이터2"} childValue ={setChildData}/>

            {/* 현재 state 중 childData 변수를 화면에 출력 */}
            <h3>Child2컴포넌트에서 전달받은 데이터 : {childData}</h3>
        </div>
    )
}

export default Parents2;

Child2.jsx

//Child2.jsx

import React from "react";

// Child2 컴포넌트는 부모 컴포넌트에서 전달받은 2개의 키가 존재함
// props에 value, childValue 키를 전달 받았고, 해당 키에 1:1로 매칭된 데이터가 존재한다.
// value : "부모 데이터", childValue : setState() 함수가 들었음

// 자식 => 부모 : 부모 컴포넌트가 제공하는 키에 부모가 사용하고 있는 함수에 데이터를 넣어준다.
// 부모는 자기 함수를 실행하면서 전달받은 데이터가 실행이 된다.

// 부모가 넘겨줄 때 이름은 childValue고 실제 부모가 사용할 때는 childData가 실행된다.

function Child2(props){
    
    // 부모 컴포넌트에서 props를 통해서 전달받은 함수를 실행
    // 부모 컴포넌트에서 전달받은 함수가 부모의 state를 수정할 수 있는 setState()함수임
    const sendData = () => {
        props.childValue('자식 데이터');
    }
    
    return(
        <div>
            <h3>Child2 컴포넌트 영역</h3>
            {/*부모 컴포넌트에서 props를 통해서 전달받은 데이터 출력*/}
            <p>부모 컴포넌트에서 전달받은 데이터 : {props.value}</p>
            {/*버튼 클릭 시 지정한 함수 실행 - 버튼 클릭 이벤트*/}
            <button onClick={sendData} className={"btn btn-primary"}>클릭 시 데이터 전달</button>
            
        </div>
    )
}

export default Child2;

부모컴포넌트 부모의 state에 setState를 자식 컴포넌트에 전달. 자식 컴포넌트는 부모의 setState를 실행할 수 있다. =>
















0개의 댓글