글 작성자: 망고좋아
반응형

📖 오늘 배운 내용 - 2022.01.04

  • dangerouslySetInnerHTML
  • 카카오 api 사용

 

📝 이론 시간 코드

import './App.css';
import {useState} from 'react';

function Header(props){
  console.log('Header');
  function onClickHandler(evt){ 
    evt.preventDefault();
    props.onChangeMode('WELCOME');
  }
  return <header>
      <h1><a href="index.html" onClick={onClickHandler}>{props.title}</a></h1>
    </header>
}

function Nav(props){
  let lis = []
  function clickHandler(evt){
    evt.preventDefault();
    props.onChangeMode('READ', Number(evt.target.dataset.id));    
  }
  for(let i = 0; i < props.data.length; i++){
    let d = props.data[i];
    lis.push(<li key={d.id}><a href={'/read/'+d.id} data-id={d.id} onClick={clickHandler}>{d.title}</a></li>)
  }
  return <nav>
    <ol>
        {lis}
    </ol>
  </nav>
}

function Article({title, body}){
  console.log('Article');
  return <article>
    <h2>{title}</h2>
    {body}
  </article>
}

function Create(props){
  function submitHandler(evt){
    evt.preventDefault();
    let title = evt.target.title.value;
    let body = evt.target.body.value;
    props.onSubmit(title, body)
    console.log(title);
  }
  return <article>
    <h2>Create</h2>
    <form onSubmit={submitHandler}>
      <p><input type="text" name="title" placeholder="title" /></p>
      <p><textarea name="body" placeholder="body"></textarea></p>
      <p><input type="submit" value="create" /></p>
    </form>
  </article>
}

function Update(props){
  const [title, setTitle] = useState(props.title);
  const [body, setBody] = useState(props.body);

  function submitHandler(evt){
    evt.preventDefault();
    let title = evt.target.title.value;
    let body = evt.target.body.value;
    props.onSubmit(title, body)
    console.log(title);
  }
  return <article>
    <h2>Update</h2>
    <form onSubmit={submitHandler}>
      <p><input type="text" name="title" placeholder="title" value={title} onChange={evt=>setTitle(evt.target.value)} /></p>
      <p><textarea name="body" placeholder="body" value={body} onChange={evt=>setBody(evt.target.value)}></textarea></p>
      <p><input type="submit" value="update" /></p>
    </form>
  </article>
}

function App() {
  const [mode, setMode] = useState('WELCOME');
  const [id, setId] = useState(null);
  const [nextId, setNextId] = useState(4);
  const [topics, setTopics] = useState([
    {id:1, title:'html', body:'html is ...'},
    {id:2, title:'css', body:'css is ...'},
    {id:3, title:'js', body:'js is ...'}
  ]);

  function changeModeHandler(_mode, _id){
    if(_mode === 'DELETE'){
      let newTopics = [];
      for(let i = 0; i < topics.length; i++){
        if(topics[i].id !== id){
          newTopics.push(topics[i]);
        }
      }
      setTopics(newTopics);
      setMode('WELCOME');
      return;
    }
    setMode(_mode);
    setId(_id);
  }

  let articleTag;
  if(mode === 'WELCOME'){
    articleTag = <Article title="Welcome" body="Hello, React!"/>
  } else if(mode === 'READ'){
    let title = null;
    let body = null;
    for(let i = 0; i < topics.length; i++){
      if(topics[i].id === id){
        title = topics[i].title;
        body = topics[i].body;
      }
    }
    articleTag = <Article title={title} body={body}/>
  } else if(mode === 'CREATE'){

    articleTag = <Create onSubmit={(_title, _body)=>{
      let newTopic = {id:nextId, title:_title, body:_body}
      let newTopics = [...topics];
      newTopics.push(newTopic);
      setTopics(newTopics);
      setMode('READ');
      setId(nextId);
      setNextId(nextId+1);
    }}></Create>
  } else if(mode === 'UPDATE'){
    let title = null;
    let body = null;
    for(let i = 0; i < topics.length; i++){
      console.log(topics[i].id, id);
      if(topics[i].id === id){
        title = topics[i].title;
        body = topics[i].body;
      }
    }

    articleTag = <Update title={title} body={body} onSubmit={(title, body)=>{
      let newTopics = [...topics];
      for(let i = 0; i < newTopics.length; i++){
        if(newTopics[i].id === id){
          newTopics[i].title = title;
          newTopics[i].body = body;
        }
      }
      setTopics(newTopics);
      setMode('READ');      
    }}></Update>
  }

  return (
    <>
      <Header title="WEB" onChangeMode={changeModeHandler}/>
      <Nav data={topics} onChangeMode={changeModeHandler}/>
      {articleTag}
      <Control onChangeMode={changeModeHandler} selectedId={id}/>
    </>
  );
}

function Control(props){
  function ClickHandler(evt){
    evt.preventDefault();
    props.onChangeMode('CREATE');
  }
  function ClickUpdateHandler(evt){
    evt.preventDefault();
    props.onChangeMode('UPDATE', props.selectedId);
  }
  let contextUI = null;
  if(props.selectedId > 0){
    contextUI = <>
      <li><a href={'/update/'+props.selectedId} onClick={ClickUpdateHandler}>update</a></li>
      <li>
        <form onSubmit={evt=>{
          evt.preventDefault();
          props.onChangeMode('DELETE');
        }}>
          <input type="submit" value="delete" />
        </form>
      </li>
    </>
  }
  return <ul>
    <li><a href="/create" onClick={ClickHandler}>create</a></li>
    {contextUI}
  </ul>
}
export default App;
  • 삭제는 link를 사용하지 않고 form을 사용한다.

 

📝 State의 위치 찾기

  • React는 항상 컴포넌트 계층 구조를 따라 아래로 내려가는 단방향 데이터 흐름을 따른다.
  • state를 기반으로 렌더링 하는 모든 컴포넌트를 찾는다.
  • 공통된 부모 컴포넌트를 찾는다.
  • 공통된 부모 혹은 더 상위에 있는 컴포넌트가 state를 가져야 한다.
  • state를 소유할 적절할 컴포넌트를 찾지 못했다면, 단순 state를 소유하는 컴포넌트를 생성하여 공통된 부모 컴포넌트의 상위 계층에 추가한다.

 

📝 역방향으로 데이터 흐름 추가하기

import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

function ProductCategoryRow(props) {
  return (
    <tr>
      <th colSpan="2">{props.category}</th>
    </tr>
  );
}

function ProductRow(props) {
  let name;
  if (props.product.stocked) {
    name = props.product.name;
  } else {
    name = <span style={{ color : "red" }}>{props.product.name}</span>
  }

  return (
    <tr>
      <td>{name}</td>
      <td>{props.product.price}</td>
    </tr>
  );
}

// 이제 추가적인 데이터를 더 받으니, 이름이 뭔지 확인할 필요가 있음.
// forEach 부분에서, 모든 데이터를 다 받지 않음.
// 이 경우, 어떻게 처리하는게 좋을까?

function ProductTable(props) {
  const rows = [];
  let lastCategory = null;


  props.products.forEach((product) => {
    // filterText가 물건 이름에 포함이 되어 있나??
    // 만약에 체크가 되어 있다면, 이 상품의 stocked 상태도 봐야 하지 않을까?

    // "sa", "sandbox" "asa"
    // 1. if (!product.name.includes(props.filterText)) return;
    // 2. product.name.indexOf(props.filterText) === -1 return;
    if (!product.name.includes(props.filterText)) return;
    if (props.inStockOnly && !product.stocked) return;


    if (product.category !== lastCategory) {

      rows.push(<ProductCategoryRow 
        category={product.category}
        key={product.category}
      />);
    }
    rows.push(<ProductRow product={product} key={product.name} />);
    lastCategory = product.category
  })

  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Price</th>
        </tr>
      </thead>
      <tbody>{rows}</tbody>
    </table>
  );
}

// 이벤트만 추가하면 됨...

function SearchBar(props) {
  return (
    <form>
      <input
        type="text"
        placeholder="Search..."
        value={props.filterText}
        onChange={(e) => {
          props.onFilterTextChange(e.target.value);
        }}
      />
      <p>
        <input
          type="checkbox"
          checked={props.inStockOnly}
          onChange={(e) => {
            props.onInStockChange(e.target.checked);
          }}
        />

          Only show products in stock
      </p>
    </form>
  );
}

// 필터 정보는 ProductTable도 알아야 하고, SearchBar도 알아야 함.
// 이 바뀌는 정보를 관리할 수 있는 방법이 있을까?
// Hint : SearchBar가 기록을 바꾸니, 여기에다 추가적으로 뭔가를 더 전달하자.

function FilterableProductTable(props) {
  const [filterText, filterTextChange] = useState("");
  const [inStockOnly, inStockOnlyChange] = useState(false);

  return (
    <div>
      <SearchBar 
        filterText={filterText}
        inStockOnly={inStockOnly}
        onFilterTextChange={filterTextChange}
        onInStockChange={inStockOnlyChange}
      />
      <ProductTable 
        products={props.products} 
        filterText={filterText}
        inStockOnly={inStockOnly}
      />
    </div>
  );
}

const PRODUCTS = [
  {category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
  {category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
  {category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
  {category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
  {category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
  {category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];

ReactDOM.render(
  <FilterableProductTable products={PRODUCTS} />,
  document.getElementById("container")
);

serviceWorker.unregister();
  • props를 부모에게 전달할 수 없으니까 부모에서 setChange 함수를 props로 넘겨주고 자식에서 그 함수를 실행.
  • 그리고 부모는 보여주는 곳에 props로 전달해주면 된다.

 

💡 오늘 깨달은 것

  • dangerouslySetInnerHTML은 브라우저 DOM에서 innerHTML을 사용하기 위한 React의 대체 방법이다. 일반적으로 코드에서 HTML을 설정하는 것은 사이트 간 스크립팅 공격에 쉽게 노출될 수 있기 때문에 위험하다.
function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

 

📌 참고

반응형