환경설정, 수정 및 배포
npx serve -s build(파일위치)
: build폴더안에 있는 index.html을 서비스해주는 웹서버 실행
-s : 사용자가 어떤 경로로 들어오든 index.html을 서비스
리액트는 사용자 정의태그를 만드는 기술이다.
function Header(){ //대문자로 시작
return <header>
<h1><a href="/">WEB</a></h1>
</header>
}
function Nav(){ //대문자로 시작
return <nav>
<ol>
<li><a href="/read/1">html</a></li>
<li><a href="/read/1">html</a></li>
<li><a href="/read/1">html</a></li>
</ol>
</nav>
}
function Article(){ //대문자로 시작
return <article>
<h2>welcome</h2>
hello,web
</article>
}
function App(){
return (
<div>
<Header></Header> //컨퍼넌트
<Nav></Nav> //컨퍼넌트
<Article></Article> //컨퍼넌트
</div>
);
}
export default App;
사용자정의 태그(함수)를 만들때는 시작이 무조건 대문자이여한다.
속성을 PROP
라고 한다.
속성을 사용하고 싶으면 넣고싶은 값을 함수의 파라미터 값(props)로 넣는다.
함수안의 html안에서 중괄호로 감싸주어 이를 속성으로 받는다. ex){props.title}
이런식으로 하면 하나의 함수로 값이 달라지는 html 로 만들 수 았다
function Nav(props){ //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>
}
function App(){
const topics = [
{id:1, title:'html', body:'html is...'},
{id:1, title:'css', body:'css is...'},
{id:1, title:'js', body:'js is...'}
]
return(
<div>
<Header title="REACT"></Header>
<Nav topics={topics}></Nav>
<Article title="welcome" body="Hello, WEB"></Article>
</div>
);
}
export default App;
위의 예시는 li
를 topics라는 변수안에 배열로 넣고 length길이만큼 for문을 돌려서 li
태그를 생성한다.
{t.id}, {t.title}라는 데이터 값을 지정해 주어서 데이터 값이 달라도 하나의 함수로 만들 수 있다.
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} onClick={event=>{
event.preventDefault();
porps.onChangeMode(event.target.id);
}}>{t.title}</a>
</li>)
}
return <nav>
<ol>
{lis}
</ol>
</nav>
}
function App(){
const topics = [
{id:1, title:'html', body:'html is...'},
{id:1, title:'css', body:'css is...'},
{id:1, title:'js', body:'js is...'}
]
return(
<div>
<Header title="WEB" onchangeMode={()=>{
alert('Header');
}}></Header>
<Nav topics={topics} onchangeMode={()=>{
alert(id);
}}></Nav>
<Article title="welcome" body="Hello, WEB"></Article>
</div>
);
}
export default App;
자동으로 생성한 태그에는 key
가 필요하다.
key
값은 element
에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야한다.
key
는 다른 항목들 사이에서 고유하게 식별할 수 있는 것을 사용한다.
이벤트객체를 처음에 넣는다. (es6문법 사용)
event.preventDefault();
로 이벤트 리로딩을 막는다.
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 App() {
// const _mode = useState('welcome'); state의 초기값
//const mode = _mode[0]; 0번째 원소로 읽음
//const setMode = _mode[1]; 1번째원소인 setMode를 통해 mode를 바꿀 수 있음
const [mode, setMode] = useState(''); //위의 축약형 보통 이걸 사용
const [id, setId] = useState(null); //선택한 값이 id와 일치하는지 보기위해
const topics = [
{ id: 1, title: 'html', body: 'html is ...' },
{ id: 2, title: 'css', body: 'css is ...' },
{ id: 3, title: 'javascript', body: 'javascript is ...' },
];
let content = 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>;
}
return (
<div className="App">
<Header
title="WEB"
onChangeMode={() => {
// mode = 'WELCOME'; 읽을때
setMode('WELCOME'); //바꿀때
}}
></Header>
<Nav
topics={topics}
onChangeMode={(_id) => {
setMode('READ');
setId(_id);
}}
></Nav>
{content}
</div>
);
}
export default App;
mode의 값이 바뀌더라도 App은 새로 로딩되지않기때문에 state를 사용한다.
useState의 인자는 state의 초기값
useState는 배열 0번째는 상태의 값을 읽을때 쓰는 데이터
1번째는 상태의 값을 변경할때 쓰는 함수
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=>{
//onSubmit은 submit버튼을 눌렀을때 form에서 발생하는 이벤트
event.preventDefault();
const title = event.target.title.vlaue; //여기서 event.target는 form태그
const body = event.target.body.vlaue;
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"/></p>
</form>
</article>
}
function App() {
const [mode, setMode] = useState('');
const [id, setId] = useState(null);
const [nextId, setNextId] = useState();
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;
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>;
}else if(mode === 'CREATE'){ //mode가 CREATE 일때
content = <Create onCreate={(_title, _body)=>{
const newTopic = {id:nextId, title:_title, body:_body}
const newTopics = [...topics] //
topics.push(newTopic);
setTopics(newTopics);
//현재와 복제본이 다르다면 컨퍼넌트 새로 렌더링
setMide('READ'); //새로 만든것의 상세페이지 보기
setId(nextId); //현재 추가한 글의 id 지정
setNextId(nextId+1); //다음에 추가할 글을 위해 id지정
}}></Create>
}
return (
<div className="App">
<Header
title="WEB"
onChangeMode={() => {
setMode('WELCOME');
}}
></Header>
<Nav
topics={topics}
onChangeMode={(_id) => {
setMode('READ');
setId(_id);
}}
></Nav>
{content}
<a href="/create" onClick={event=>{ //생성버튼 mode가 CREATE로 바뀜
event.preventDefault();
setMode('CREATE');
}}>Create</a>
</div>
);
}
export default App;
const [value, setValue] = useState(PRIMITIVE);
string, number, bigint, boolean, undefined, symbol, null
const [value, setValue] = useState(Object);
object, array
object의 경우
newValue ={...value} //데이터를 복제한다
newVlaue 변경 // 복제본 변경
setValue(newValue) //변경된 복제본을 실행
array경우
newValue =[...value] //데이터를 복제한다
newVlaue 변경 // 복제본 변경
setValue(newValue) //변경된 복제본을 실행
객체와 같이 복합적인 데이터일 경우
const [value, setValue] = useState([1]);
value.push(2); //오리지널 데이터를 바꿈
setValue(value); //오리지널 데이터가 바뀌면서 현재와 같아서 컨퍼넌트가 렌더링 안됨
=> 해결방법
const [value, setValue] = useState([1]);
newValue = [...value] //배열일 경우 대괄호
newValue.push(2);
setValue(newValue); //컨퍼넌트 다시 실행
UPDATE = CREATE + READ
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.vlaue;
const body = event.target.body.vlaue;
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"/></p>
</form>
</article>
}
function Update(props){
const [title, setTitle] = useState(props.title);
const [body, setBody] = useState(props.body);
//props의 데이터를 state데이터로 환승
return <article>
<h2>Update</h2>
<form onSubmit={event=>{
event.preventDefault();
const title = event.target.title.vlaue;
const body = event.target.body.vlaue;
props.onUpdate(title,body);
}}>
<p><input type="text" name="title" placeholder="title" value={title} onChange={event=>{
setTitle(event.target.value);
//state는 컴포넌트로 바꿀 수 있다
//키보드로 입력할때마다 새로 렌더링
//가장 최근에 변경된 값을 받아와서 바꿈
}}/></p>
<p><textarea name="body" placeholder="body"value={body} onChange={event=>{
setBody(event.target.value); //키보드로 입력할때마다
}}></textarea></p>
<p><input type="submit" value="Update"/></p>
</form>
</article>
}
function App() {
const [mode, setMode] = useState('');
const [id, setId] = useState(null);
const [nextId, setNextId] = useState();
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; //update를 위한 변수 생성
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>
}else if(mode === 'CREATE'){
content = <Create onCreate={(_title, _body)=>{
const newTopic = {id:nextId, title:_title, body:_body}
const newTopics = [...topics]
topics.push(newTopic);
setTopics(newTopics);
//현재와 복제본이 다르면 새로 렌더링
setMide('READ'); //새로 만든것의 상세페이지 보기
setId(nextId);
setId(nextId+1);
}}></Create>
}else if(moede === 'UPDATE'){ //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){ //id가 일치하는 것이 현재 선택한 topic
newTopics[i] = updatedTopic; //교체
break;
}
}
setTopics(newTopics);
setMode('READ');
}}></Update>
}
return (
<div className="App">
<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;
contextControl = <> //그룹핑하는 용도의 빈 태그
<li><input type="button" value="Delete" onClick={()=>{
const newTopics = [] //오리지널과는 다른 데이터
for(let i=0; i<toptics.length; i++){
if(topics[i].id !== id){
newTopics.push(topics[i]); //일치하지 않는 topic만 push
}
}
setTopics(newTopics);
setMode('WELCOME');
}} />
</li>
</>
참고자료
: https://www.youtube.com/watch?v=AoMv0SIjZL8&list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7