2023.01.26 Update
Create, Update를 ul로 묶어서 목록화 시키기.
<ul>
<li>
<a href='/create' onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a>
</li>
<li><a href='/update'>Update</a></li>
</ul>
update 기능은 상세보기 페이지에서만 보여지게.
contextControl 변수를 선언하고
let contextControl = null;
update 버튼은 mode가 'READ'일때만 나타나도록 위치를 변경하고
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>
contextControl = <li><a href='/update'>Update</a></li>
}
Create버튼 밑에 contextControl을 위치시킨다.
<ul>
<li>
<a href='/create' onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a>
</li>
{contextControl}
</ul>
update에 고유한 id 추가.
contextControl = <li><a href={'/update/' + id}>Update</a></li>
update에 이벤트 추가.
setMode로 'UPDATE' 변경.
contextControl = <li><a href={'/update/' + id} onClick={event=>{
event.preventDefault();
setMode('UPDATE');
}}>Update</a></li>
else if(mode === 'UPDATE') {
content = <Update></Update>
}
Create와 유사하다.
function Update(props) {
return <article>
<h2>Update</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
props.onUpdate(title, body);
}}>
<p><input type="text" name="title" placeholder="title" /></p>
<p><textarea name='body' placeholder='body'></textarea></p>
<p><input type="submit" value="Update"></input></p>
</form>
</article>
}
Update 컴포넌트는 props로 onUpdate를 호출하기 때문에 사용자에게도 onUpdate를 주자.
else if(mode === 'UPDATE') {
content = <Update onUpdate={(title, body)=>{
}}></Update>
}
수정은 기존의 제목과 본문값을 가지고 와야하기 때문에
Update 컴포넌트의 form이 기존내용을 가지고 있기 위해서는
Update 컴포넌트가 title, body를 기본적으로 가지고 있어야한다.
else if(mode === 'UPDATE') {
content = <Update title={} body={} onUpdate={(title, body)=>{
}}></Update>
}
이전에 READ에서 title과 body를 얻어왔던 방법을 사용하자.
Update 컴포넌트가 실행되기 전에 for문을 추가해서 title과 body를 얻어서
컴포넌트에 전달한다.
else if(mode === 'UPDATE') {
let title, body = null;
for(let i=0; i<topics.length; i++) {
if(topics[i].id === id) {
title = topics[i].title;
body = topics[i].body;
}
}
content = <Update title={title} body={body} onUpdate={(title, body)=>{
}}></Update>
}
이제 title, body를 화면에 출력해보자.
Update 컴포넌트 입력창에 value로 값을 전달한다.
<p><input type="text" name="title" placeholder="title" value={props.title}/></p>
<p><textarea name='body' placeholder='body' value={props.body}></textarea></p>
그러면 입력창에 제목과 본문이 출력된다.
그런데...
입력창에 입력을 할 수가 없다.
왜냐하면 props라는 데이터는 사용자가 컴포넌트로 전달한 일종의 명령이다.
props로 전달되는 title,body의 내용이 고정되어 있기때문에
변경할 수 없다.
props는 사용자(외부자)가 내부자로 전달하는 값.
state는 내부자가 사용하는 값이기 때문에 변경이 가능하다.
따라서 props를 state로 변경해야한다.
Update 컴포넌트에
title,body를 state로 변경하고
입력창에도 그 값으로 변경한다.
function Update(props) {
const [title, setTitle] = useState(props.title);
const [body, setBody] = useState(props.body);
return <article>
<h2>Update</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
props.onUpdate(title, body);
}}>
<p><input type="text" name="title" placeholder="title" value={title}/></p>
<p><textarea name='body' placeholder='body' value={body}></textarea></p>
<p><input type="submit" value="Update"></input></p>
</form>
</article>
}
하지만 이렇게 해도 입력창에 입력은 되지 않는다.😒
이럴 때는 onChange이벤트를 사용한다.
리액트의 onChange이벤트는 html의 onChange이벤트와 다르다.
html의 onChange이벤트는 값이 바뀌고 마우스포인트가 바깥으로 빠져나갈 때 onChange가 호출되는데
리액트의 onChange이벤트는 값이 입력될 때마다 호출된다.
그렇기 때문에 입력된값을 얻어서 setTitle을 진행해 새로운 state로 변경한다.
setTitle(event.target.value);
<p><input type="text" name="title" placeholder="title" value={title} onChange={event=>{
console.log(event.target.value);
setTitle(event.target.value);
}}/>
</p>
입력이 가능해진다.
body도 변경
<p><textarea name='body' placeholder='body' value={body} onChange={event=>{
setBody(event.target.value);
}}></textarea>
</p>
이제 update버튼을 클릭하면 onSubmit이 호출되면서
title, body 값을 onUpdate로 전달한다.
그러면 onUpdate에 title과 body에 수정된 값이 들어온다.
변경된 값으로 topics를 변경하면 된다.
변경된 값으로 updatedTopic 객체를 만든다.
id의 경우 'READ'를 선택하고 update가 진행되기 때문에
'READ'가 선택됐을때 자동으로 id가 세팅된다.
그래서 id는 바로 가져오면 된다.
content = <Update title={title} body={body} onUpdate={(title, body)=>{
const updatedTopic = {id:id, title:title, body:body}
}}></Update>
이제 updatedTopic를 topics에 넣으면 되는데
수정하려고하는 topics는 배열이기 때문에
const newTopics = [...topics] 를 이용해서 변경.
for문을 이용해서 id가 일치하는 것을 찾고
setTopics 진행.
상세보기로 이동하기 위해 setMode('READ')도 추가해준다.
content = <Update title={title} body={body} onUpdate={(title, body)=>{
const newTopics = [...topics]
const updatedTopic = {id:id, title:title, body:body}
for(let i=0; i<newTopics.length; i++) {
if(newTopics[i].id === id) {
newTopics[i] = updatedTopic;
break;
}
}
setTopics(newTopics);
}}></Update>
이렇게 하면 업데이트 완료 ~
✨ 전체코드
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 Update(props) {
const [title, setTitle] = useState(props.title);
const [body, setBody] = useState(props.body);
return <article>
<h2>Update</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.value;
const body = event.target.body.value;
props.onUpdate(title, body);
}}>
<p><input type="text" name="title" placeholder="title" value={title} onChange={event=>{
console.log(event.target.value);
setTitle(event.target.value);
}}/></p>
<p><textarea name='body' placeholder='body' value={body} onChange={event=>{
setBody(event.target.value);
}}></textarea></p>
<p><input type="submit" value="Update"></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;
let contextControl = 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>
contextControl = <li><a href={'/update/' + id} onClick={event=>{
event.preventDefault();
setMode('UPDATE');
}}>Update</a></li>
} 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>
} else if(mode === 'UPDATE') {
let title, body = null;
for(let i=0; i<topics.length; i++) {
if(topics[i].id === id) {
title = topics[i].title;
body = topics[i].body;
}
}
content = <Update title={title} body={body} onUpdate={(title, body)=>{
const newTopics = [...topics]
const updatedTopic = {id:id, title:title, body:body}
for(let i=0; i<newTopics.length; i++) {
if(newTopics[i].id === id) {
newTopics[i] = updatedTopic;
break;
}
}
setTopics(newTopics);
setMode('READ');
}}></Update>
}
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}
<ul>
<li>
<a href='/create' onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a>
</li>
{contextControl}
</ul>
</div>
);
}
export default App;