설치 과정에서의 오류 해결
https://velog.io/@jyun9807/react-project-%EC%83%9D%EC%84%B1-error
index.js → 입구 파일, 여기에 작성한 대로 동작하게 된다.
이때, 아래의 코드는
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
App이라는 태그가 id="root"인 태그로 렌더링하라는 코드
배포 파일 생성 → npm run build
build 폴더에 있는 index.js 파일 실행 → npx serve -s build
React는 사용자 정의 태그를 만드는 기술이다.
컴포넌트에 속성(prop)을 어떻게 추가하는지
props는 객체
반복적인 li 태그의 경우
이때, Nav는 매개변수로 topics 배열을 받는다.
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 href={'/read/' + t.id}>{t.title}</a></li>)
}
return <nav>
<ol>
{lis}
</ol>
</nav>
}
자동생성한 태그의 경우, 고유한 key 속성을 가져야한다.
<Header title="REACT" onChangeMode={() => {
alert('Header');
}}></Header>
function Header(props) {
return <header>
<h1>
<a href="/" onClick={(e) => {
e.preventDefault();
props.onChangeMode();
}}>{props.title}</a>
</h1>
</header>
}
<Nav topics={topics} onChangeMode={(id) => {
alert(id);
}}/>
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={(e) => {
e.preventDefault();
props.onChangeMode(e.target.id);
}}>{t.title}</a>
</li>)
}
return <nav>
<ol>
{lis}
</ol>
</nav>
}
prop과 state 모두 이 값이 변경되면 새로운 return 값을 만들어 UI를 바꾼다.
useState()는 배열을 return
const _mode = useState('WELCOME');
const mode = mode[0];
const setMode = mode[1];
// 위 세줄과 동일코드
const [mode, setMode] = useState('WELCOME');
APP 컴포넌트가 다시 실행되면서 setMode로 바뀐 값이 들어간다.
클릭하는 목차에 따라 아래의 Content 내용이 바뀌어야 한다.
1. Nav 태그에서 클릭한 내용에 따라 id 값이 바뀌도록 한다.
<Nav topics={topics} onChangeMode={(_id) => {
setMode('READ');
setId(_id);
}}/>
이때, _id의 값은
props.onChangeMode(Number(e.target.id));
e.target.id로 현재 이벤트 태그의 id 값을 가져온다.
id의 경우, string값이므로 숫자로 변환한다.
2. id 값이 바뀌었다면 topics 배열에서 해당되는 데이터를 가져와 화면에 보여준다.
const [id, setId] = useState(null); //초기값 null
...
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>
}
content가 해당되는 내용을 보여준다.
Create를 누르면 title과 body를 작성해서 화면에 보여지도록 만들어보자
1. App 컴포넌트에 Create 링크를 만들고 링크 클릭 시 Create 컴포넌트가 동작하도록 하자
<a href="/create" onClick={(e) => {
e.preventDefault();
setMode('CREATE');
}}>Create</a>
2. Create 컴포넌트 만들기
function Create(props) {
return <article>
<h2>Create</h2>
<form onSubmit={(e) => {
e.preventDefault();
const title = e.target.title.value; //title의 value 값을 가져올 수 있다.
const body = e.target.body.value;
props.onCreate(title, body);
}}>
<p><input type='text' name='title' placeholder='title'></input></p>
<p><textarea name='body' placeholder='body'></textarea></p>
<p><input type='submit' value='Create' /></p>
</form>
</article>
}
3. Create 컴포넌트로부터 submit 정보를 받아서 사용자 정의 로직 실행
const [nextId, setNextId] = useState(4);
const [topics, setTopics] = useState([
{id:1, title:'html', body:'html is ...'},
...
]);
...
else if(mode === 'CREATE') {
content = <Create onCreate={(_title, _body) => {
// topics 배열에 새로운 원소가 추가되어야 함
const newTopic = {id:nextId, title:_title, body:_body};
const newTopocs = [...topics];
newTopocs.push(newTopic);
setTopics(newTopocs);
setMode('READ');
setId(nextId);
setNextId(nextId + 1);
}}/>
}
value의 타입이 string, number, bigint, boolean, undefined, symbol, null 일 경우
const [value, setValue] = useState(PRIMITIVE);
value의 타입이 object, array 일 경우
const [value, setValue] = useState(Object);
newValue = {...value} or [...value]
newValue 변경
setValue(newValue)
React는 setValue를 호출했을 때, original 데이터와 새로 들어온 데이터가 같은 데이터인지 확인하고 만약 같은 데이터라면 컴포넌트를 굳이 다시 렌더링하지 않는다.
Update를 누르면 title과 body를 수정할 수 있도록 만들어보자
1. App 컴포넌트에 Update 링크를 만들고 링크 클릭 시 Update 컴포넌트가 동작하도록 하자
이때, mode가 'READ'일 때만 Update 컴포넌트가 보이도록 하기 위해 else-if문 안에 contextControl라는 변수를 만들어 보여준다.
let contextControl = null;
...
else if (mode === 'READ') {
...
contextControl = <li><a href={"/update/" + _id} onClick={(e) => {
e.preventDefault();
setMode('UPDATE');
}}>Update</a></li>;
}
그리고 Update 클릭 시, mode가 'UPDATE'로 변해서 update 로직을 타게 된다.
2. Update 컴포넌트 만들기
function Update(props) {
const [title, setTitle] = useState(props.title);
const [body, setBody] = useState(props.body);
return <article>
<h2>Update</h2>
<form onSubmit={(e) => {
e.preventDefault();
const title = e.target.title.value; //title의 value 값을 가져올 수 있다.
const body = e.target.body.value;
props.onUpdate(title, body);
}}>
<p><input type='text' name='title' placeholder='title' value={title} onChange={(e) => {
console.log(e.target.value);
setTitle(e.target.value);
}}></input></p>
<p><textarea name='body' placeholder='body' value={body} onChange={(e) => {
setBody(e.target.value);
}}></textarea></p>
<p><input type='submit' value='Update' /></p>
</form>
</article>;
}
React에서 props라는 데이터는 사용자가 그 컴포넌트로 전달한 일종의 명령이다. 따라서 props의 값을 바꾸려면 state로 바꾸어야 한다.
위 코드에서는 const [title, setTitle] = useState(props.title);
와 const [body, setBody] = useState(props.body);
로 바꾸어 주었다.
state는 컴포넌트 내부에서 변경가능한 데이터이다.
따라서 onChange() 함수에서 키보드를 입력할 때마다 새로운 value로 바뀌고 컴포넌트가 새로 렌더링된다.
위 코드에서는 onChange={(e) => { console.log(e.target.value); setTitle(e.target.value);
와 onChange={(e) => { setBody(e.target.value); }}
로 작성해주었다.
3. Update 컴포넌트로부터 submit 정보를 받아서 사용자 정의 로직 실행
const [id, setId] = useState(null);
const [topics, setTopics] = useState([
{id:1, title:'html', body:'html is ...'},
...
]);
...
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);
}}/>
}
update의 경우, 원래 작성되어 있던 title, body의 값들을 보여줘야 하기 때문에 Update 컴포넌트의 props로 전달해주었다.
Delete 버튼을 누르면 해당 topic을 삭제하도록 구현해보자
1. Delete 버튼 구현하고 삭제 로직 작성하기
contextControl = <>
<li><a href={"/update/" + id} onClick={(e) => {
e.preventDefault();
setMode('UPDATE');
}}>Update</a></li>
<li><input type="button" value="Delete" onClick={() => {
const newTopics = [];
for(let i=0; i<topics.length; i++) {
if (id !== topics[i].id) {
newTopics.push(topics[i]);
}
}
setTopics(newTopics);
setMode('WELCOME');
}}/></li>
</>;
<> </>
의 경우 복수의 태그를 그룹핑하는 용도이다.setTopics(newTopics);
해주면 삭제된 상태가 업데이트 된다.