[TIL] 엘리스 SW 엔지니어 트랙 Day 052
글 작성자: 망고좋아
반응형
📖 오늘 배운 내용 - 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 · Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
📌 참고
반응형
'프로그래밍 > Today I Learned' 카테고리의 다른 글
[TIL] 엘리스 SW 엔지니어 트랙 Day 054 (0) | 2022.01.13 |
---|---|
[TIL] 엘리스 SW 엔지니어 트랙 Day 053 (0) | 2022.01.11 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 051 (0) | 2022.01.03 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 050 (0) | 2022.01.03 |
[TIL] 엘리스 SW 엔지니어 트랙 Day 049 (0) | 2021.12.31 |
댓글
이 글 공유하기
다른 글
-
[TIL] 엘리스 SW 엔지니어 트랙 Day 054
[TIL] 엘리스 SW 엔지니어 트랙 Day 054
2022.01.13 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 053
[TIL] 엘리스 SW 엔지니어 트랙 Day 053
2022.01.11 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 051
[TIL] 엘리스 SW 엔지니어 트랙 Day 051
2022.01.03 -
[TIL] 엘리스 SW 엔지니어 트랙 Day 050
[TIL] 엘리스 SW 엔지니어 트랙 Day 050
2022.01.03