React - #6 실습

임다이·2023년 12월 11일

React

목록 보기
7/11

Ex09.jsx

컴포넌트끼리 값을 주고 받기

  • Ex09.jsx : 상위 요소 (부모)
  • components -> Ex09Item.jsx : 하위 요소 (자식)

  • Ex09.jsx
import React, { useState } from 'react'
import Ex09Item from './components/Ex09Item'

const Ex09 = () => {

    /*
    컴포넌트끼리 값을 주고 받기
    - Ex09.jsx : 상위 요소 (부모)
    - components -> Ex09Item.jsx : 하위 요소 (자식)

    ** 상위에서 하위로! 실습
    1. input에 onChange 이벤트를 걸어준다 => changeData 함수 실행
    2. 이벤트 객체를 받아와서 사용자의 입력값을 console에 확인
    3. inputTitle이라는 state에 해당 입력값을 세팅
    4. inputTitle이라는 state 값을 Ex09Item에게 전달 - props
    5. 입력할 때 마다 어쩌고저쩌고 대신 나의 입력값이 들어가도록
    */

    const [inputTitle, setInputTitle] = useState("");

    const changeData =  (e)=>{
        console.log(e.target.value);
        setInputTitle(e.target.value);
    }

    // ---------------------------------------------------------

    const [childTitle, setChildTitle] = useState("");

    const chageDataFromChild = (data) => {
        console.log('chageDataFromChild Function Called', data);
        setChildTitle(data)
    }

  return (
    <div>
        <h1>양방향 데이터 통신</h1>
        <h3>상위에서 하위로</h3>
        <input type='text' onChange={changeData}></input>
        <Ex09Item text={inputTitle} chageDataFromChild={chageDataFromChild}/>

        <span>하위에서 상위로 넘어온 값은 {childTitle} 입니다.</span>
    </div>
  )
}

export default Ex09
  • Ex09Item.jsx
import React from 'react'

const Ex09Item = ({text, chageDataFromChild}) => {
  return (
    <div>
        상위 컴포넌트에서 입력받은 값은 {text} 입니다.

        <hr/>

        <h3>하위에서 상위로</h3>
        <input type='text' onChange={(e)=>{
            chageDataFromChild(e.target.value)
        }}/>
    </div>
  )
}

export default Ex09Item


1

색상 클릭 시 밑에 색상 클릭한 색상으로 바뀌기



  • Ex10.jsx
import React, { useState } from 'react'
import { colorContext } from './context/Ex10ColorContext'

import ColorList from './components/Ex10/ColorList'
import ColorResult from './components/Ex10/ColorResult'

const Ex10 = () => {

    /*
    Context 란?
    - 리액트 컴포넌트 간에 값을 전역적으로 공유할 수 있게 해주는 기능
    - 우리에게는 데이터를 주고받는 props가 있는데 왜 context를 써야하는가?
    A. props로만 데이터를 전달하면 깊숙하게 위치한 컴포넌트에 데이터를 전달하는 경우
       여러번 연달아서 props를 설정하게 됨 => 불편하고, 실수가 잦음
       이러한 현상을 props drilling 이라고 부른다.

       그래서 우리는 context로 전역적으로 데이터를 관리한다.

    [Context 만드는 순서]
    1) context 파일을 만들어준다.
       - createContext
       - 꼭! export 해주기

    2) context를 사용할 공간에 import {context} 해주기!

    3) context Provider로 감싸주기
       - Provider 안에 value라는 속성
       - value 안에 우리가 전달하고자 하는 데이터, 저장하고자 하는 데이터 등등을 넣는다.
    */

       const [choiceColor, setChoiceColor] = useState('red');

  return (
    <colorContext.Provider value={{choiceColor, setChoiceColor}}>

        <h1>변경할 색상을 고르시오.</h1>
        <ColorList/>

        <hr/>

        <h1>선택한 색상은</h1>
        <ColorResult/>
    </colorContext.Provider>
  )
}

export default Ex10
  • context / Ex10ColorContext.js
import { createContext } from 'react';

export const colorContext = createContext(null);
  • components / ColorList.jsx
import React, { useContext } from 'react'
import { colorContext } from '../../context/Ex10ColorContext'

const ColorList = () => {

    let color = ["red", "orange", "yellow", "green", "blue"]

    const {setChoiceColor} = useContext(colorContext)

    const colorClick = (e) => {
        console.log(e.target.style.backgroundColor);
        setChoiceColor(e.target.style.backgroundColor)
    }

  return (
    <div style={{display : 'flex'}}>
        {color.map(item => (
            <div
                key={item}
                style={{
                    width : "100px",
                    height : "100px",
                    background : `${item}`
                }
            } onClick={colorClick}/>
        ))}
    </div>
  )
}

export default ColorList
  • ColorResult.jsx
import React, { useContext } from 'react'
import { colorContext } from '../../context/Ex10ColorContext'

const ColorResult = () => {

    const {choiceColor} = useContext(colorContext)
  return (
    <div style={{
        width : '100px',
        height : '100px',
        background : `${choiceColor}`
    }}>

    </div>
  )
}

export default ColorResult


2

다크모드


  • Ex11.jsx
import React, { useState } from 'react'
import { ThemeContext } from './context/Ex11ThemeContext'

import Header from './components/Ex11/Header'
import Content from './components/Ex11/Content'
import Footer from './components/Ex11/Footer'
import './ex11.css'

const Ex11 = () => {

    const [darkMode, setDarkMode] = useState(false);

  return (
    <div>
        <ThemeContext.Provider value={{darkMode, setDarkMode}}>
            <Header/>
            <Content/>
            <Footer/>
        </ThemeContext.Provider>
    </div>
  )
}

export default Ex11
  • ex11.css
* {
    box-sizing: border-box;
    margin: 0;
    font-family: sans-serif;
  }
  
  .page {
    width: 100vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
  }
  
  .header {
    width: 100%;
    height: 80px;
    border-bottom: 2px solid gray;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  
  .content {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 30px;
  }
  
  .footer {
    width: 100%;
    height: 80px;
    border-top: 2px solid gray;
    display: flex;
    justify-content: flex-end;
    align-items: center;
  }
  
  .button {
    padding: 10px;
    margin-right: 30px;
  }
  • Ex11ThemeContext.js
import { createContext } from "react";

export const ThemeContext = createContext(null);
  • Header.jsx
import React, { useContext } from "react";
import { ThemeContext } from "../../context/Ex11ThemeContext";

const Header = () => {

  const {darkMode} = useContext(ThemeContext)

  return (
    <header
      className="header"
      style={{
        backgroundColor: darkMode ? "black" : "skyblue",
        color: darkMode ? "white" : "black"
      }}
    >
      <h1>스마트인재개발원</h1>
    </header>
  );
};

export default Header;
  • Content.jsx
import React, { useContext } from "react";
import { ThemeContext } from "../../context/Ex11ThemeContext";

const Content = () => {

  const {darkMode} = useContext(ThemeContext)

  return (
    <div
      className="content"
      style={{
        backgroundColor: darkMode ? "black" : "white",
        color: darkMode ? "white" : "black",
      }}
    >
      <h1>지각하지 않기! 결석하지 않기!</h1>
    </div>
  );
};

export default Content;
  • Footer.jsx
import React, { useContext } from "react";
import { ThemeContext } from "../../context/Ex11ThemeContext";

const Footer = () => {

  const {darkMode, setDarkMode} = useContext(ThemeContext)

  const toggleTheme = () => {
    console.log('darkMode', darkMode);
    setDarkMode(!darkMode)
    // setDarkMode(darkMod ? false : true)

    // if(darkMode == false){
    //   setDarkMode(true)
    // }else {
    //   setDarkMode(false)
    // }
  };

  return (
    <div>
      <footer
        className="footer"
        style={{
          backgroundColor: darkMode ? "black" : "lightgray"
        }}
      >
        <button className="button" onClick={toggleTheme}>
          {darkMode ? <sapn>Light Mode</sapn> : <sapn>Dark Mode</sapn>}
        </button>
      </footer>
    </div>
  );
};

export default Footer;

  • Light Mode
  • Dark Mode

3

Todo List




  • Ex12.jsx
import React, { useState } from 'react'
import { TodoContext } from './context/Ex12TodoContext'
import List from './components/Ex12/List'
import AddItem from './components/Ex12/AddItem'
import './ex12.css'

const Ex12 = () => {

    /** 할 일 리스트가 누적되어있는 배열 (state 형식) */
    const [todos, setTodos] = useState([
        {text : "물 마시기", completed : false, key : 1}
    ]);

    // STEP 1. 할 일 추가하기
    const [newTodo, setNewTodo] = useState("");

    /** 새로운 newTodo 데이터를 todos 배열에 추가하는 함수 */
    const handleNewTodoAddition = () => {
        console.log('handleNewTodoAddition function', newTodo)
        setTodos([
            ...todos,
            {text : newTodo,
             completed : false,
             key : todos[todos.length-1].key + 1
            }
        ]);
        setNewTodo("")
    }

    /** todolist를 삭제시켜 줄 함수 */
    const handleTodoDelete = (delKey) => {
        console.log('handleNewTodoDelete function', delKey)
        const filteredList = todos.filter(item => item.key !== delKey)
        setTodos(filteredList)
    }

    /** 완료한 한 일에 체크 혹은 반대의 경우 체크 해제 toggle 함수 */
    const handleTodoToggle = (ckKey) => {
        console.log('handleTodoToggle function', ckKey);

        // * find 배열함수
        let targetTodo = todos.find(item => item.key == ckKey)
        console.log('targetTodo', targetTodo);

        if(targetTodo) {
            targetTodo.completed = !targetTodo.completed
            setTodos([...todos])
        }
    }

  return (
    <TodoContext.Provider value={{todos,
                                newTodo,
                                setNewTodo,
                                handleNewTodoAddition,
                                handleTodoDelete,
                                handleTodoToggle}}>
        <div className='todo-container'>
            <h1>📃TODO LIST🖊</h1>
            <List/>
            <AddItem/>
        </div>
    </TodoContext.Provider>
  )
}

export default Ex12
  • ex12.css
@font-face {
  font-family: "GmarketSansMedium";
  src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2001@1.1/GmarketSansMedium.woff")
    format("woff");
  font-weight: normal;
  font-style: normal;
}

* {
  font-family: "GmarketSansMedium";
}
.todo-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(120deg, pink 0%, #c2e9fb 100%);
  border-radius: 20px;
  margin: 3%;
  padding: 10%;

  box-sizing: border-box;
}

h1 {
  font-weight: 900;
}
li {
  list-style: none;
}

button {
  height: 30px;
  font-size: 0.8em;
  background-color: rgb(231, 181, 190);
  border: 0px;
}

input[type="checkbox"] {
  margin: 3%;
}

li{
  display: flex;
  align-items: center;
  justify-content: center;
}
.todo-text{
  /* background-color: red; */
  width: 300px;
  display: block;
}

.list-container{
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

input[type="text"]{
  width: 300px;
}
  • Ex12TodoContext.js
import { createContext } from "react";

export const TodoContext = createContext(null);
  • List.jsx
import React, { useContext } from 'react'
import { TodoContext } from '../../context/Ex12TodoContext'
import ListItem from './ListItem';

const List = () => {

    const {todos} = useContext(TodoContext)
    console.log('todos', todos);

  return (
    <div>
        <table>
            <tbody>
                {todos.map(item => <ListItem key={item.key} todo={item}/>)}
            </tbody>
        </table>
    </div>
  )
}

export default List
  • AddItem.jsx
import React, { useContext } from 'react'
import { TodoContext } from '../../context/Ex12TodoContext'

const AddItem = () => {

    const {newTodo, setNewTodo, handleNewTodoAddition} = useContext(TodoContext)
  return (
    <div>
        <input value={newTodo} type='text' onChange={(e)=>{setNewTodo(e.target.value)}}/>
        <button onClick={handleNewTodoAddition}>Add</button>
    </div>
  )
}

export default AddItem
  • ListItme.jsx
import React, { useContext } from 'react'
import { TodoContext } from '../../context/Ex12TodoContext'

const ListItem = ({todo}) => {

    const {handleTodoDelete, handleTodoToggle} = useContext(TodoContext)

  return (
    <>
    <tr>
        <td><input type='checkbox' checked={todo.completed} onChange={()=>{handleTodoToggle(todo.key)}}/></td>
        <td>
            <label style={{
                textDecoration : todo.completed ? "line-through" : "none"
            }}>
                <span className='todo-text'>{todo.text}</span>
            </label>
        </td>
        <td><button onClick={()=>{handleTodoDelete(todo.key)}}>Delete</button></td>
    </tr>
    </>
  )
}

export default ListItem


profile
노는게 제일 좋아~!

0개의 댓글