본문 바로가기
TIL with Programmers

[TIL] 11/1 props, 모달창 만들기

by 보먀 2024. 11. 1.
728x90
반응형

1. props

일반 함수의 매개변수와 비슷하다고 생각하면 된다. props 는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 넘길 때 사용한다. (역방향, 자식 -> 부모로는 불가능)

 

 

1.1. 함수형 컴포넌트에서 props 사용하기

 

부모 컴포넌트에서 "맑음" 을 넘겨주면 자식 컴포넌트에서 받아서 사용할 수 있다. 타입스크립트는 타입이 존재하기 때문에 자바스크립트와는 다르게 props 의 타입을 지정해줘야 한다

// App.tsx -> 부모 컴포넌트

function App() {

  return (
    <div className='container'>
      <MyWeather weather={"맑음"}></MyWeather>
    </div>
  );
}

export default App;
// MyWeather.tsx -> 자식 컴포넌트

interface MyProps {
    weather: string;
    children: React.ReactNode; // 컴포넌트의 자식요소
}

const MyWeather: React.FC<MyProps> = (props) => {

    return (
        <div>
            오늘의 날씨는 {props.weather} 입니다. 
        </div>
    )
}

export default MyWeather;

 

 

아래의 jsx 가 부모 컴포넌트의 일부라고 할 때, <MyWeather> 태그 사이에 있는 것을 부모 컴포넌트의 children 이라고 한다. 

<MyWeather weather={"맑음"}>부모컴포넌트의 children</MyWeather>

 

그래서 자식컴포넌트에서는 props.children 으로 가져와서 사용할 수 있다. 

<div>{props.children}</div>

 

 

그리고 props 의 값을 가져올 때도 구조분해할당을 사용하면 더 간단하게 사용할 수 있다.

interface MyProps {
    weather: string;
    children: React.ReactNode; // 컴포넌트의 자식요소
}

const MyWeather: React.FC<MyProps> = ({children, weather}) => {

    return (
        <div>
            오늘의 날씨는 {weather} 입니다. 
            <div>{children}</div>
        </div>
    )
}

export default MyWeather;

 

 

1.2. 클래스형 컴포넌트에서 props 사용하기

 

this 를 통해 props 를 전달받을 수 있다. this 는 해당 컴포넌트 클래스 인스턴스 자체를 말한다. 

import { Component } from 'react';

interface MyProps {
    weather: string;
    children: React.ReactNode; // 컴포넌트의 자식요소
}

class MyWeather extends Component<MyProps> {
    render() {
        const {children, weather} = this.props;
        
        return (
            <div>
                오늘의 날씨는 {weather} 입니다.
                <div>{children}</div>
            </div>
        )
    }
}

export default MyWeather;

 

 

 

2. 모달창 띄우기

 

모달창은 리액트 부트스트랩에 만들어져 있는 것을 가져와서 사용하였다.

 

우선 props 의 타입을 지정해주었다.

  • show -> 모달창을 보여줄 것인지 아닌지
  • todo -> 투두 자체
  • handleClose -> 모달창을 닫을 때 사용할 함수
// TodoModal.tsx

import { Modal } from 'react-bootstrap';

type Todo = {
    id: number;
    text: string;
    isChecked: boolean;
};

type TodoModalProps = {
    show: boolean;
    todo: Todo | null;
    handleClose: () => void;
}

const TodoModal : React.FC<TodoModalProps> = ({show, todo, handleClose}) => {

    return (
        <div>
            <Modal show={show} onHide={handleClose} centered>
                <Modal.Header closeButton>
                    <Modal.Title>Todo 상세정보</Modal.Title>
                </Modal.Header>
                <Modal.Body>{todo?.text}</Modal.Body>
            </Modal>
        </div>
    );
}

export default TodoModal;

 

 

showDetail -> 모달창을 보여줄 것인지 여부를 나타내는 state

selectedTodo -> 현재 사용자가 어떤 투두 리스트를 클릭하였는지 TodoModal 컴포넌트로 보내주기 위한 state

 

handleTodoClick 함수는 span 태그가 클릭되면 클릭된 태그가 어떤 투두 리스트인지 TodoModal 에 알려주기 위한 state 인 selectedTodo 의 state 를 바꿔준다. (클릭된 투두 리스트로) 그리고 세부 사항 모달창을 띄워주기 위해 showDetail 을 true 로 바꾸어준다. 

 

handleCloseDetail 은 모달창을 닫는 X 표시를 누르면 실행이 되는데, showDetail 을 false 로 바꾸어 모달창을 닫도록 한다. 

// Todolist.tsx

import { useState } from "react";
import { Button } from 'react-bootstrap';
import TodoModal from "./\bTodoModal";

type Todo = {
    id: number;
    text: string;
    isChecked: boolean;
};

const TodoList: React.FC = () => {
    const title: string = "오늘 할 일";
    const [todos, setTodos] = useState<Todo[]>([
        {id: 0, text: '잠자기', isChecked: false }, 
        {id: 1, text: '공부하기', isChecked: false }, 
        {id: 2, text: '미팅하기', isChecked: false }
    ]);
	
    // ...

    const [showDetail, setShowDetail] = useState<boolean>(false);
    const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);

    const handleTodoClick = (todo: Todo) => {
        setShowDetail(true)
        setSelectedTodo(todo)
    }

    const handleCloseDetail = () => {
        setShowDetail(false);
    }

    return (
    <div>
        <h1>{title}</h1>
            <p></p>
            <div className="container">
                <div>
                    <input type="text" 
                        placeholder="할 일 입력" 
                        style={{marginRight: '10px',
                        writingMode: 'horizontal-tb'}}
                        onChange={(e) => setNewTodo(e.target.value)}
                    />
                    <Button onClick={addTodo}>추가</Button>     
                </div>
                <p></p>
                <div className="board">
                    <ul>
                        {
                            todos.map((todo) => 
                                <li key={todo.id}>
                                    <input type="checkbox" 
                                    onChange={() => {
                                        handleCheckedChange(todo.id);
                                    }}></input>
                                    <span onClick={() => handleTodoClick(todo)}>
                                        {
                                            todo.isChecked ? <del>{todo.text}</del> : todo.text
                                        }
                                    </span>
                                    <button className="del-button"
                                        onClick={() => removeTodo(todo.id)}
                                    >삭제</button>
                                </li>
                            )
                        }
                    </ul>
                </div>
            </div>
            <TodoModal show={showDetail} todo={selectedTodo} handleClose={handleCloseDetail}></TodoModal>
        </div>
    )
}

export default TodoList;
728x90
반응형