React 2022 개정판(기초_복습)
코드 실행
npm start
에서 해당 port에 이미 다른게 존재한다는 내용 있어도 y 입력해서 넘어가주면 다른 포트로 개발 서버 열어준다.
react
의 큰 특징이 사용자 정의 컴포넌트
이다. 즉, 이걸 활용해 코드 수정한다.
- 이전 코드
// App.js
import ReactDOM from 'react-dom'
ReactDom.render(
<React.StrictMode><App/></React.StrictMode>,
document.getElementById('root')
)
- 현재 코드
// App.js
import ReactDOM from 'react-dom/client'; // 다름
const root = ReactDOM.createRoot(document.getElementById('root')); // 다름
root.render(
// <React.StrictMode> 참고로 StrictMode때문에 2번 렌더링 되는경우 있으니 꼭 인지할것.
<div>
<App />
</div>
// </React.StrictMode>
);
리액트는 npx로 생성한 파일이 너무 크기 때문에 배포할 땐 꼭 build 를 한다.
npm run build
는 배포판 버전으로 build 폴더로 생성된다.
- 부가 옵션으로
npx serve -s build
입력시 build 폴더에 index.html이 젤 먼저 실행되게 한다.
PROP(=속성)
src, width, height가 다 PROP
이다. 만약 사용자 컴포넌트
에서도 이런 속성을 다루고 싶다면?!
<img src="image.jpg" width="100" height="100"/>
이름은 사람들도 다 props
로 보통 명칭함. {}
로 감싸면 표현식
- 참고로 이것과 더불어 spread, 구조분해를 정리한 포스팅을 볼 것. 관련문서
function Header(props) {
return <header>
<h1><a href="/">{props.title}</a></h1>
</header>
}
// 속성을 넣어서 만들었음
// 아래 실행
<Header title="REACT"></Header>
List
같이 배열같은걸 props
로 넘기는것도 간단하게 가능.
- 다만,
key
를 반복문 내에서 꼭 넘겨줘야 warning이 안뜬다.
function Nav(props) {
const lis = []
for(let i=0; i<props.list.length; i++){
let t = props.list[i];
lis.push(<li key={t.id}><a>{t.title}</a></li>)
}
}
// 반복문을 이용해 만들었음
// 실행
const list = [{id:1, title:'html'}, {id:2, title:'css'}, ...]
<Nav list = {list}></Nav>
이벤트
onclick
부분 처럼 이벤트
를 사용자 컴포넌트
에서도 만들고 싶다면?!
<input type="button" onclick="alert('hi')">
마찬가지로 props
로 접근하면 된다.
- a태그 클릭시 해당 list값의 id값이 alert 되는 구조이다.
-
event.preventDefault();
기본 이벤트 Default값 방지(실행 안하는거임)
function Nav(props) {
const lis = []
for(let i=0; i<props.list.length; i++){
let t = props.list[i];
lis.push(<li key={t.id}><a id={t.id} onClick={event=>{
event.preventDefault();
props.onChangeMode(event.target.id);
}}>{t.title}</a></li>)
}
}
// event.target은 event를 유발시킨 a태그를 의미
// 실행
const list = [{id:1, title:'html'}, {id:2, title:'css'}, ...]
<Nav list = {list} onChangeMode={(id)=>{
alert(id);
}}></Nav>
state
prop
와, state
가 변경시 React
는 재렌더를 해서 컴포넌트를 다시 생성
-
prop
는 컴포넌트를 사용하는 외부자를 위한 데이터 -
state
는 컴포넌트를 만드는 내부자를 위한 데이터
아래의 코드는 Header, Nav
태그를 누를때 마다 mode
가 바껴서 해당하는 content
가 출력되는 코드이다. 그러나 정상 작동하지 않는다.
- prop나 state가 변경될시 함수가 재렌더 된다고 하였다. 그런데 현재 코드는 재렌더가 일어나지를 않으니까 처음 App()함수 return한 값을 그대로 사용하고 있을 뿐이다.
function App() {
const mode = 'WELCOME';
const list = [{id:1, title:'html'}, {id:2, title:'css'}, ...];
let content = null;
if(mode === 'WELCOME') {
content = <Header title="WELCOME"/>
} else if(mode === 'READ') {
content = <Header title="READ"/>
}
return (
<div>
<Header~~~~~~~ onChangeMode={() =>{
mode = 'WELCOME';
}}</Header>
<Nav ~~~~~~~ onChangeMode={(id) => {
mode = 'READ';
}}</Nav>
{content}
</div>
);
};
어떻게 해결을 하나? useState
라는 리액트에서 제공하는 상태관리 훅을 사용하면 된다. 관련문서
useState('WELCOME')
은 2개의 원소를 가지는 객체이다.
// 수정코드
function App() {
const [mode, setMode] = useState('WELCOME');
const list = [{id:1, title:'html'}, {id:2, title:'css'}, ...];
let content = null;
if(mode === 'WELCOME') {
content = <Header title="WELCOME"/>
} else if(mode === 'READ') {
content = <Header title="READ"/>
}
return (
<div>
<Header~~~~~~~ onChangeMode={() =>{
setMode('WELCOME');
}}</Header>
<Nav ~~~~~~~ onChangeMode={(id) => {
setMode('READ');
}}</Nav>
{content}
</div>
);
};
Header, Nav
클릭시 잘 변경되는거 확인하였고, Nav
의 각 a태그들
클릭시 변경되는 부분을 코드로.
-
_id
는 state의id
와 구분하기 위함. -
_id
는 클릭한 a태그의 id값을 가짐. -
list
요소의id
는 숫자이다.
function Nav(props){
// ... 생략
props.onChangeMode(Number(event.target.id)); // number로 바꿔줘야 함.
// ... 생략
}
// ... 생략
function App() {
const [mode, setMode] = useState('WELCOME');
const [id, setId] = useState(null);
const list = [{id:1, title:'html'}, {id:2, title:'css'}, ...];
let content = null;
if(mode === 'WELCOME') {
content = <Header title="WELCOME"/>
} else if(mode === 'READ') {
// 이부분 for문으로 구성
let title, body = null;
for(let i = 0 ; i<list.length; i++){
if(list[i].id === id) {
title = list[i].title;
body = list[i].body;
}
}
content = <Header title={title} body={body}/>
}
return (
<div>
<Header~~~~~~~ onChangeMode={() =>{
setMode('WELCOME');
}}</Header>
<Nav ~~~~~~~ onChangeMode={(_id) => {
setMode('READ');
setId(_id);
}}</Nav>
{content}
</div>
);
};
CRUD
Create
- Create를 클릭하면 mode를 CREATE로 바꿔서 화면에 해당 content 출력하려고 함.
// a태그를 이렇게 추가해서 위에서 else if(mode === 'CREATE')... 이런식으로 해결하면 된다.
<a onClick={(event) => {
event.preventDefault();
setMode('CREATE');
}}>Create/<a>
그림처럼 구성이 될거고, 내부적으로
form
으로 해결하면 된다.
form
과 더불어 중요한 개념
이 여기서 나타난다.
- 2번째 그림이 복제를 하는 이유인데, value가 원시 데이터이며 배열인데 거기에 push를 한다는것은원시 데이터인 value 배열이 변경된거다. 여기서 setValue(value);를 해봤자 원시데이터의 value와 setValue의 매개변수로 넣은 value가 값이 동일하단건 당연한 얘기이다. 따라서 재렌더를 하지않는다.
이처럼 기존 숫자나 이런것 말고, 범객체(객체, 배열 등)
들은 state 처리때 복제본
을 써야한다.
- event.target은 당연히 form태그
// Create 태그에 form태그 이용 + prevenDefault활용
function Create() {
return <article>
<h2>Create</h2>
<form onSubmit={event=>{
event.prevetDefault();
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>
}
// App함수
// ... 생략
const [nextId, setNextId] = useState(4) // 임시로 추가해서 이용
const [list, setList]~~로 state로 감싸기.
// ... 생략
} else if(mode === 'CREATE'){
content = <Create onCreate={(_title, _body) => {
const newList = {id:nextId, title:_title, body:_body}
const newLists = [...list] // 복제
newLists.push(newList)
setList(newLists); // 데이터 변경시 렌더링
setMode('READ');
setId(nextId);
setNextId(nestId+1);
}}></Create>
}
Update
Update = Create + Read
Create와 유사하게 작성하면 되고, 자세한건 코드를 참고. Update는 난이도가 조금 있는 부분이다.
- 주의할점은 기존의 값(props)이 form태그 내의 input태그의 value값으로 value={props.title} 이런식으로 가면 일종의 이것은 명령어이기 때문에 input태그에 아무것도 입력을 할 수가 없다.
- 따라서 state로 환승하는것이 매우 중요하다
- 또한 onChange이벤트와 같이 이를 활용해 setState를 통해 state값을 input태그의 사용자가 입력한 값으로 바꿔서 input태그에 value값에 다시 나타내는것 또한 중요하다.
- 참고로 WELCOM 모드는 Update, Delete를 보여주지 않고 READ 모드가 보여준다.
Delete
input에 type을 button으로 추가하였다. 코드도 매우 간단하게 추가해서 끝낸다.
// 최종 코드
import logo from './logo.svg';
import './App.css';
import {useState} from 'react';
function Article(props){
return <article>
<h2>{props.title}</h2>
{props.body}
</article>
}
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 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=>{
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, 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:'javascript', body:'javascript is ...'}
]);
let content = null;
let contextControl = null;
if(mode === 'WELCOME'){
content = <Article title="Welcome" body="Hello, WEB"></Article>
} else if(mode === 'READ'){
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 = <Article title={title} body={body}></Article>
contextControl = <>
<li><a href={'/update/'+id} onClick={event=>{
event.preventDefault();
setMode('UPDATE');
}}>Update</a></li>
<li><input type="button" value="Delete" onClick={()=>{
const newTopics = []
for(let i=0; i<topics.length; i++){
if(topics[i].id !== id){ // 삭제!
newTopics.push(topics[i]);
}
}
setTopics(newTopics);
setMode('WELCOME');
}} /></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);
setMode('READ');
setId(nextId);
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)=>{
console.log(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="WEB" onChangeMode={()=>{
setMode('WELCOME');
}}></Header>
<Nav topics={topics} onChangeMode={(_id)=>{
setMode('READ');
setId(_id);
}}></Nav>
{content}
<ul>
<li><a href="/create" onClick={event=>{
event.preventDefault();
setMode('CREATE');
}}>Create</a></li>
{contextControl}
</ul>
</div>
);
}
export default App;
댓글남기기