2023.01.26 Create
Create 클릭 시
제목과 본문내용을 입력하고
li로 추가되는 어플리케이션을 만들어보자.
Create 페이지로 이동하는 a 태그 선언.
<a href='/create'>Create</a>
Create 클릭 시 mode가 'CREATE'로 바뀌고
새로운 ui가 나타나도록 할것이다.
a태그에 이벤트 추가.
a태그 기본기능 막고 클릭 시 mode가 'CREATE'로 변경.
<a href='/create' onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a>
mode가 'CREATE'일 때
if문에서 content내용을 변경해주면 된다.
else if(mode === 'CREATE') {
content = <Create></Create>
}
CREATE는 li도 만들어야하기 때문에
따로 컴포넌트를 만들어서 사용하자.
입력값을 전달하기 위해 form태그 사용.
input,textarea 사용하고 이 태그들은 인라인 소요이기 때문에
가로정렬이 된다.
p 태그로 감싸서 세로정렬을 진행.
function Create() {
return <article>
<h2>Create</h2>
<form>
<p><input type="text" name="title" placeholder="title" /></p>
<p><textarea name='body' placeholder='body'></textarea></p>
<p><input type="submit" value="Create"></input></p>
</form>
</article>
}
이렇게 하면 create 버튼 클릭시 제목과 본문을 입력할 수 있는 ui가 나타난다.
Create 컴포넌트를 이용하는 이용자가 생성버튼을 눌렀을때
후속작업을 할 수있게 인터페이스를 제공해보자.
onCreate( )함수 추가하고 onCreate는 title,body를 받는다.
else if(mode === 'CREATE') {
content = <Create onCreate={(title, body)=>{
}}></Create>
}
onCreate함수를 호출할 수 있도록 Create 컴포넌트 수정.
form태그에 onSubmit( ) prop를 제공한다.
onSubmit( ) 은 submit 버튼을 눌렀을 때 form 태그에서 발생하는 이벤트이다.
form태그는 submit을 하면 페이지가 리로드 되기 때문에 기능을 막아야한다.
event.preventDefault(); 사용.
그 후 form 태그의 title,body의 value를 얻어와야한다.
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
}}>
이제 title, body의 value를 Create 컴포넌트 사용자에게 공급해야한다.
사용자는 onCreate를 이용해서 submit정보를 공급받는다.
그렇기 때문에 Create 컴포넌트에 props를 주고 onCreate( )를 호출하고
매개변수로 title과 body를 준다.
function Create(props) {
return <article>
<h2>Create</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
props.onCreate(title, body);
}}>
<p><input type="text" name="title" placeholder="title" /></p>
<p><textarea name='body' placeholder='body'></textarea></p>
<p><input type="submit" value="Create"></input></p>
</form>
</article>
}
이제 topics변수에 새로운 원소를 추가해서 li목록이 추가되도록 해보자.
topics를 state로 승격시키기.
const [topics, setTopics] = useState([
{id:1, title: 'html', body:'html is...'},
{id:2, title: 'css', body:'css is...'},
{id:3, title: 'jsvascript', body:'jsvascript is...'},
]);
topics의 새로운 원소 만들기.
newTopic 객체 생성하고 객체의 id가 필요하다.
else if(mode === 'CREATE') {
content = <Create onCreate={(_title, _body)=>{
const newTopic = {title: _title, body:_body}
}}></Create>
}
id값을 별도로 관리하기 위해 nextId state 생성하고 newTopic 객체에 id를 추가한다.
const[nextId, setNextId] = useState(4);
else if(mode === 'CREATE') {
content = <Create onCreate={(_title, _body)=>{
const newTopic = {id:nextId, title: _title, body:_body}
}}></Create>
}
newTopic 생성이 완료 되었으니 이제 topics에 추가를 해야한다.
topics.push(newTopic);
topics는 값을 읽을때 사용하는 것이기 때문에 추가할 수 없다.
그렇다면 setTopics를 쓰면 될까?
setTopics(topics);
이렇게 해도 아무일도 일어나지 않는다..!!!
✨
PRIMITIVE(원시) 타입이면 지금까지 했던 방식을 사용하면 된다.
(String,Number,Boolean,null)
하지만 Object 타입이라면 newValue = {...value} 를 사용해야한다.
(Object,Array)
newValue = {...value}
...value : 데이터를 복제해서 newValue에 넣는다.
newValue를 변경한다.(복제본)
setValue(newValue)
newValue를 넣으면 컴포넌트가 다시 실행된다.
위 내용을 참고하여 코드를 다시 수정해보자.
const newTopics = [...topics] //복제본
newTopics.push(newTopic); //복제본에 푸쉬
setTopics(newTopics); //topics에 추가
리액트는 오리지널 topics와 새로들어온 newTopics를 비교해서
내용이 다르면 컴포넌트를 다시 렌더링해준다.
이렇게 하면 완성!
create 클릭 - 글작성 - 글상세보기로 바로이동하기
content = <Create onCreate={(_title, _body)=>{
const newTopic = {id:nextId, title: _title, body:_body}
const newTopics = [...topics] //복제본
newTopics.push(newTopic); //복제본에 푸쉬
setTopics(newTopics); //topics에 추가
//새 li추가 후 mode를 READ로 변경하여 상세보기로 이동
setMode('READ');
//현재글을 nextId로 지정
setId(nextId);
//다음 글추가할때를 위해 현재 id+1
setNextId(nextId+1);
}}></Create>
const [value, setValue] = useState([1]);
배열
value.push(2);
오리지널 데이터 변경
setValue(value);
오리지널 데이터 입력
리액트는 setValue를 호출했을때 오리지널 데이터와 새로들어온 데이터가
같으면 컴포넌트를 다시 렌더링하지 않는다.
const [value, setValue] = useState(1);
원시타입
setValue(2);
새로운 데이터 입력
오리지널 데이터와 새로 들어온 데이터가 다르기 때문에 컴포넌트를 다시 렌더링한다.
const [value, setValue] = useState([1]);
배열
newValue = [...value]
오리지널 데이터 복제
newValue.push(2);
복제한 데이터 변경
setValue(newValue);
변경한 데이터를 set
✨ 전체코드
import logo from './logo.svg';
import './App.css';
import { useState } from 'react';
function Header(props) {
return <header>
<h1><a href='/' onClick={(event)=>{
event.preventDefault();
props.onChangeMode();
}}>{[props.title]}</a></h1>
</header>
}
function Nav(props){
const lis = [ ]
for(let i=0; i<props.topics.length; i++) {
let t = props.topics[i];
lis.push(<li key={t.id}>
<a id={t.id} href={'/read/'+t.id} onClick={(event)=>{
event.preventDefault();
props.onChangeMode(Number(event.target.id));
}}>{t.title}</a>
</li>)
}
return <nav>
<ol>
{lis}
</ol>
</nav>
}
function Article(props) {
return <article>
<h2>{props.title}</h2>
{props.body}
</article>
}
function Create(props) {
return <article>
<h2>Create</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
props.onCreate(title, body);
}}>
<p><input type="text" name="title" placeholder="title" /></p>
<p><textarea name='body' placeholder='body'></textarea></p>
<p><input type="submit" value="Create"></input></p>
</form>
</article>
}
function App() {
// const _mode = useState('WELCOME');
// const mode = _mode[0];
// const setMode = _mode[1];
const [mode, setMode] = useState('WELCOME');
// console.log('_mode', _mode);
//li클릭시에 state로 변경하기 위해 id,setId 변수선언, 현재 값이 선택되지 않았기 때문에 null
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: 'jsvascript', body:'jsvascript is...'},
]);
let content = null;
if(mode === 'WELCOME'){
content = <Article title="Welcome" body="Hello, WEB"></Article>
const value = [1];
console.log('value :' + value);
value.push(2);
console.log('value : ' + value);
} else if(mode === 'READ') {
let title, body = null; //title,body 초기화
//반복문 이용해서 선택한 id(state)와 일치하는 li의 id를 찾아서 그 값을 얻어옴
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;
}
}
content = <Article title={title} body={body}></Article>
} else if(mode === 'CREATE') {
content = <Create onCreate={(_title, _body)=>{
const newTopic = {id:nextId, title: _title, body:_body}
const newTopics = [...topics] //복제본
newTopics.push(newTopic); //복제본에 푸쉬
setTopics(newTopics); //topics에 추가
//새 li추가 후 mode를 READ로 변경하여 상세보기로 이동
setMode('READ');
//현재글을 nextId로 지정
setId(nextId);
//다음 글추가할때를 위해 현재 id+1
setNextId(nextId+1);
}}></Create>
}
return (
<div>
<Header title="REACT" onChangeMode={()=>{
// alert('Header');
// mode = 'WELCOME' 값을 바꿀때는 setMode를 사용한다.
setMode('WELCOME');
}}></Header>
<Nav topics={topics} onChangeMode={(_id)=>{
// alert(id);
// mode = 'READ'
setMode('READ');
//onChangeMode 매개변수로 _id를 받고 setId를 통해 id를 변경
setId(_id);
}}></Nav>
{content}
<a href='/create' onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a>
</div>
);
}
export default App;