변수 넣을 땐 {중괄호}
변수 선언은 function 내, return 위에
style 넣을 땐 style = {{스타일명: '값', 스타일명2: '값2'}}
div {
box-sizing: border-box; // css테두리 영역 크기 : 전체영역 적용
}
box-sizing(=css테두리 영역)에는 2가지 있음
1)border-box :지정한 CSS width 및 height를 컨텐츠 영역에만 적용합니다. border, padding, margin은 따로 계산되어 전체 영역이 설정값보다 커질 수 있음
2)content-box :지정한 CSS width 및 height를 전체 영역에 적용합니다. border, padding, margin을 모두 합산하기 때문에 컨텐츠 영역이 설정값보다 적어질 수 있음
.list {
padding-left: 20px; // 안 여백 왼쪽
text-align: left; // 텍스트의 정렬 방향 : 왼쪽
border-bottom: 1px solid grey;
//테두리 하단 색상 : 두께/스타일/색상
}
.black-nav {
display: flex; // Flex Container : 수직쌓임
background: black; // 배경색
width: 100%; // 너비
color: yellowgreen; // 글자색
padding-left: 20px;
}
display: 컨테이너의 쌓임 구성https://heropy.blog/2018/11/24/css-flexible-box/
onClick
span태그 안에다가 다음과 같이 쓴다.<span>onClick={ 함수명 또는 함수그잡채 } ></span>
이때 함수는 함수선언문이나 화살표함수를 써야 한다.
onClick={ ()=> { likesChange(likes+1)} } //또는 onClick={ function(){likesChange(likes+1)} }
state
state의 두번째 파라미터 likesChange를 정하고
이 파라미터 안에 변수의 변한 값을 likesChange(likes+1) 이런 식으로 넣는다.function App() { ... let[likes, likesChange] = useState(0) return ( <span onClick={ ()=> { likesChange(likes+1)} }></span> ); ... } export default App;
function App() {
...
let[titles, titlesCh] = useState(['여자 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기'])
let[likes, likesChange] = useState(0)
function likesFn() {
likes += 1
}
return (
<div className="App">
<span onClick={ ()=> { likesChange(likes+1)} }>
...
<button onClick={()=>{
let titlesCopy = [...titles]
titlesCopy[0] = '남자코트추천 수정됨';
titlesCh(titlesCopy);
}}> 글 수정 </button>
<div className="list">
<h4>{post} <span onClick={ ()=> { likesChange(likes+1)} }> 좋아요👍</span> {likes} </h4>
<p>2월 17일 발행</p>
</div>
<div className="list">
<h4>{post} <span onClick={ ()=> { likesChange(likes+1)} }> 좋아요👍</span> {likes} </h4>
<p>2월 17일 발행</p>
</div>
...
</div>
);
}
export default App;
state가 array/object면 shallow copy(독립적 카피본)을 만들어서 수정해야 함
let array = [1,3,5,6] 라고 할 때
let Array = [...array] 이렇게 써주면 기존 array와는 독립적인 사본배열을 만들어서 Array라는 새로운 화살표가 사본 배열 [1,3,5,6]을 가리키게 된다.
<button onClick={()=>{
let titlesCopy = [...titles]
titlesCopy[0] = '남자코트추천 수정됨';
titlesCh(titlesCopy);
}}> 글 수정 </button>
아래 코드가 안먹는다!!
function App() {
let[titles, titlesCh] = useState(['여자 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기'])
return (
<div className="App">
<button onClick={()=>{
titles[0] = ['수정수정']
titlesCh(titles)
}}> 글 수정 </button>
<div className="list">
<h4>{post} <span onClick={ ()=> { likesChange(likes+1)} }> 좋아요👍</span> {likes} </h4>
<p>2월 17일 발행</p>
</div>
...
</div>
);
}
내 코드가 안먹었던 이유
- 기존 state == 신규 state의 경우 변경 안해주는 특성
- array와 object 함수의 특성
array 특성상 변수명 titles와 array ['여자 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기']는 따로 저장이 되고 변수명은 array를 가리키기만 한다.
titles[0] = ['수정수정']이 수정은 적용이 되어서 램에는 수정된 ['수정수정', '새해 다이어리', 'THENCE 문구편집숍 후기']로 저장이 되지만 이 array를 가리키는 변수 titles는 변경된 적이 없다.
그렇기에 titlesCh(titles)에서 변수 titles의 state가 기존과 변화가 없다고 판단하여 버튼이 안먹는 것이다.
그렇다면
<button onClick={()=>{
titles[0] = ['수정수정']
titlesCh(...titles)
}}> 글 수정 </button>
이렇게 스프레드 문법을 썼을 때 글목록이 다음과 같이 바뀌는 것은 왜 때문일까..?
https://hwb0218.tistory.com/41
"많은 div들을 한 단어로 줄이고 싶으면"
컴포넌트 만드는 법
1.function 만들고 : 다른 function "밖"에다 만들어야함!
2. return() 안에 html 담기 : return안에는 최상단div태그 1개만!
3. <함수명></함수명>쓰기
컴포넌트로 만들면 좋은 상황
1. 반복적인 html 축약할 때
2. 큰 페이지들
3. 자주 변경되는 UI같은것들
컴포넌트 단점: state가져다 쓸 때 문제 생김
다른함수의 변수를 가져다 쓸 수 없기 때문에..!
동적인 UI만드는 step
1. html css로 미리 디자인 완성
2. UI의 현재상태를 state로 저장
3. state에 따라 UI가 어떻게 보일지 (조건문같은걸로) 작성
import './App.css';
import { useState } from 'react';
function App() {
...
/////////////////2. UI의 현재상태를 state로 저장/////////////////
let [modal, setModal] = useState(false);
// 스위치역할 : 0/false/'창닫힘' 등 다양하게 상태 표현 가능
...
return (
...
<div className="list">
/////////////////3. state에 따라 UI가 어떻게 보일지 (조건문같은걸로) 작성/////////////////
<h4 onClick={()=>{
setModal(!modal)
// 변경된 상태 인식하는 부분
}}>{titles[2]}</h4>
<p>2월 17일 발행</p>
</div>
/////////////////3. state에 따라 UI가 어떻게 보일지 (조건문같은걸로) 작성/////////////////
{
modal == true ? <Modal1></Modal1> : null
// 불들어오는 기계에 해당 : 모달의 상태를 변경해주는 조건
}
// 중괄호 안에는 js if문 for문 쓸 수가 없어서 대신 삼항 연산자를 씀 : html 중간에 조건문 쓰려면 삼항연산자 쓰세요
</div>
);
}
/////////////////1. html css로 미리 디자인 완성/////////////////
function Modal1(){
return (
<div className="modal">
<h4>제목</h4>
<p>날짜</p>
<p>상세내용</p>
</div>
)
}
export default App;
map 기능
1. array 자료 갯수만큼 함수안의 코드 실행해줌
2. 함수의 파라미터는 array안에 있던 자료임[1,2,3].map(function(a){ console.log(a) } // 1 2 3 출력됨
- return에 뭐 적으면 array로 담아줌
[1,2,3].map(function(a){ return '37372' } // ['37372', '37372', '37372'] 출력됨
- map(function(a, i){ ...에서
첫번째 변수 a는 array의 요소, 두번째 변수 i는 array의 index이다.
결론
map함수의 return 문 안에 html을 넣으면 됨.
즉 아래와 같은 꼴로 작성하면 됨.{ 배열.map(function(t, i){ return ( <div > html 태그들~ </div> ) }) }
...
import './App.css';
import { useState } from 'react';
function App() {
...
return (
...
// 하드코딩 주석처리
{/* <div className="list">
<h4>{titles[0]}</h4>
<p>2월 17일 발행</p>
</div>
<div className="list">
<h4>{titles[1]}</h4>
<p>2월 17일 발행</p>
</div>
<div className="list">
<h4 onClick={()=>{
setModal(!modal)}}>{titles[2]}</h4>
<p>2월 17일 발행</p>
</div> */}
// 위 코드를 아래 반복문으로 돌림
{
// 중괄호 안에는 for 반목문을 쓸 수 없으므로 map 함수를 쓴다
titles.map(function(t, i){ // t는 요소, i는 인덱스
return (
<div className="list" key={i}> // id를 인덱스로 부여
<h4 onClick={()=>{
setModal(!modal)}}>{i} {t}</h4>
<h4>{titles[i]}</h4>
<p>2월 17일 발행</p>
</div>
)
})
}
...
</div>
);
}
...
export default App;
반복문으로 생성한 html에 key 가 없어서 warning이 뜨는 것임.
key='0' 이런식으로 id 처럼 설정해 주면 되지만
반복문의 경우 반복되는 태그별로 각자의 아이디를 가지면 좋으므로
아래 처럼 key값을 인덱스로 정해주면 warning이 사라진다.
내가 해냄! 뿌-듯
import './App.css';
import { useState } from 'react';
function App() {
...
let[likes, likesChange] = useState([0,0,0])
...
return (
...
{
// 중괄호 안에는 for 반목문을 쓸 수 없으므로 map 함수를 쓴다
titles.map(function(t, i){ // 첫번째 변수는 array안의 요소, 두번째 변수는 array의 index
return (
////////////// 가장 바깥 div의 아이디를 인덱스로 부여
<div className="list" key={i}>
...
<h4>{titles[i]}
<span onClick={ ()=> {
// likes 배열 복제
let likePushed = [...likes]
// i번째 게시글의 좋아요는 배열 인덱스 i번째 요소이므로 해당 요소에만 1 추가
likePushed[i] = likes[i]+1
// 변경된 state 반영
likesChange(likePushed)} }> 좋아요👍</span>
// 반영된 state 출력
{likes[i]}
</h4>
<p>2월 17일 발행</p>
</div>
)
})
}
...
</div>
);
}
...
export default App;
부모 👉 자식 state 전송하려면 props 문법 쓰면 되는데
1. <자식 컴포넌트 작명={state이름>
2. props 파라미터 등록 후 props.작명 사용
- props 사용시 유의사항!
- props로 일반 문자도 전송가능하다!
어렵게 생각해서 그냥 답 봄.
애플 센세가 원한것은 "모달창에서 state를 쓰기"였다.
(1) 그럼 state 정의에서 함수이름을 가져와야 하고 (아래에서는 "titlesCh")
let[titles, titlesCh] = useState(['여자 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기'])
(2) 모달에 쓸 때는 props.함수이름을 쓰고 (아래에서는 "props.titlesCh( )")
{
// 중괄호 안에는 js if문 for문 쓸 수가 없어서 대신 삼항 연산자를 씀 : html 중간에 조건문 쓰려면 삼항연산자 쓰세요
modal == true ? <Modal1 titles={titles} color={"skyblue"} titlesCh={titlesCh}></Modal1> : null // 불들어오는 기계
}
(3) 함수 괄호 안에다가 변경 할 state를 직접 써주면 됨.(아래에서는 "props.titlesCh([뭐시기, 뭐뭐, 뭐뭐뭐] )")
function Modal1(props){
return (
<div className="modal" style={({background: props.color})}>
<h4>제목 : {props.titles[0]}</h4>
<p>날짜</p>
<p>상세내용</p>
<button onClick={()=>{ props.titlesCh(['하하 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기']) }}>글수정</button>
</div>
)
}
최종 코드는
// 모달 조건문에
titlesCh={titlesCh}
// 을 추가하는 것과
// 모달 버튼을 아래처럼 쓰는 것!
<button onClick={()=>{ props.titlesCh(['하하 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기']) }}>글수정</button>
이것 뿐이었다!
지금 누른 글 제목이 모달창 안에 뜨게 하기
- html css로 미리 디자인 >> 이미 있음
- 현재UI 상태를 state에 저장해둠
- state에 따라 UI가 어떻게 보일지 작성
// function App() 상단에
let [title, setTitle] = useState(0); // 모달 창의 글 제목 state 정의
글목록 반복문에서 클릭한 제목의 아이디를 전송해주기
<h4 onClick={()=>{
setModal(!modal) // 모달창 여닫이 : modal의 state 변경
setTitle(i)}} // 클릭한 글의 id(key)로 모달창의 글제목(title)의 state 변경
>{titles[i]}
모달에서 보여주는 제목의 인덱스 = 클릭한 제목 인덱스(= 현재 모달의 상태값 setTitle)
이런식으로 되도록!
// function Modal1() 에서
<h4>{ props.titles[props.title] }</h4>
다양한 input 태그
<input type="text"></input> // 글자입력
<select></select> // 아래 화살표 선택 창
<input type="range"></input> // 범위선택 바
<input type="checkbox"></input> // 정사각 체크박스
<input type="date"></input> // 날짜 선택
<textarea></textarea> // 좀 더 큰 글자입력
input 이벤트 핸들러 많음
onChange / onInput 뭔가 입력시 코드실행
onMouseOver={} : 마우스 올렸을 때 코드 실행
onScroll={} : 스크롤 조작 할 때마다 코드 실행이런식으로 씀 <input onChange={()=> { ??? }}> </input>
🫥필요할 때마다 찾아쓰기!
input에 입력한 값 가져오는법
화살표 인수안에 e를 써넣으면 발생하는 이벤트가 담김
e.target : 이벤트 발생한 html 태그에 입력한 값
e.target.value : input에 입력한 값을 알려줌
// input에 입력한 값 가져오는 법
<input onChange={(e)=>{ console.log(e.target.value)}}></input>
이벤트 버블링 현상?
좋아요 버튼을 누르면 모달창이 같이 열리는 것은 span 태그가 div태그와 h4 태그 안에 있기 때문이다. span 태그에 있는 onClick 이벤트를 작동시키면 h4태그와 div태그까지 총 3번 작동된다.
그래서 좋아요 이벤트를 클릭했는데 상위 모달창도 함께 열리는 것!
이렇게 가장 안쪽 클릭 이벤트가 상위 html 로 펴져서 상위 모달 이벤트도 작동되게 하는 것을 이벤트 버블링 현상이라고 한다.
이벤트 버블링 현상을 막으려면 아래와 같이 e.stopPopagation()을 쓰면 된다.<div className="list" key={i}> <h4 onClick={()=>{ setModal(!modal) // 모달창 여닫이 : modal의 state 변경 setTitle(i)}} // 클릭한 글의 id(key)로 모달창의 글제목(title)의 state 변경 {titles[i]} <span onClick={ (e)=> { // 👈 e 넣어주는거 잊지말기! e.stopPropagation() // 👈이렇게 한줄 let likePushed = [...likes] likePushed[i] = likes[i]+1 likesChange(likePushed)} }> 👍</span> {likes[i]} </h4> <p>2월 17일 발행</p> </div>
input에 입력한 값 저장하려면
<input onChange={(e)=>{ console.log(e.target.value)}}></input>
이것을 아래와 같이 수정한다.
상단에 state 정의하고
let [inputValue, setInputValue] = useState('');
하단에는 이 state들을 작성해주는데
입력받은 값[e.target.value]은 변경을 감지하는 함수[setInputValue]에 넣는다!
<input onChange={(e)=>{ setInputValue(e.target.value); console.log(inputValue)}}></input>
입력값이 변수에 잘 담겼나 콘솔에 확인하는 것은 [inputValue]로 확인한다.
이때 인풋에 넣는 글자가 한 템포씩 밀려서 콘솔에 출력 되는데
이는 state 변경함수가 비동기처리(쉽게 말해 늦게 처리됨)라서 그렇다고 한다. ((setInputValue(e.target.value)이 부분))
힌트: html 직접하나 만들 필요없음. state조작해서 만들기
힌트: html 직접 지울 필요없음. state조작해서 만들기
/* eslint-disable */ // warning message 제거
import './App.css';
import { useState } from 'react';
function App() {
let[logo, setLogo] = useState("Rani's ReacBlog")
let[titles, setTitles] = useState(['여자 코트 추천', '새해 다이어리', 'THENCE 문구편집숍 후기'])// 글목록의 글제목
let [title, setTitle] = useState(0); // 모달 창의 글제목 state 정의
let [likes, setLikes] = useState([0,0,0])
let [modal, setModal] = useState(false); // 스위치역할 0/false/창닫힘 등 다양하게 상태 표현 가능
let [contents, setContents] = useState(''); // 글쓰기 input값 state 정의
return (
<div className="App">
<div className="black-nav">
<h4 style={{color:"white"}}>{logo}</h4>
</div>
{
// 중괄호 안에는 for 반목문을 쓸 수 없으므로 map 함수를 쓴다
titles.map(function(t, i){ // 첫번째 변수는 array안의 요소(t=title), 두번째 변수는 array의 index
return (
<div className="list" key={i}>
<h4 onClick={()=>{
setModal(!modal) // 모달창 여닫이 : modal의 state 변경
setTitle(i)}} // 클릭한 글의 id(key)로 모달창의 글제목(title)의 state 변경
>{titles[i]}
<span onClick={ (e)=> {
e.stopPropagation()
let likePushed = [...likes]
likePushed[i] = likes[i]+1
setLikes(likePushed)} }> 👍</span> {likes[i]}
</h4>
<p>2월 17일 발행</p>
<button onClick={()=>{
let deleteTitle =[...titles]
deleteTitle.splice(i,1)
setTitles(deleteTitle)
}}>글삭제</button>
</div>
)
})
}
<div>
<input onChange={(e)=>{ setContents(e.target.value); console.log(contents)}}></input>
<button onClick={()=>{
let newTitles = [...titles];
let newLikes = [...likes]
newTitles.unshift(contents);
newLikes.unshift(0);
console.log(newTitles)
setTitles(newTitles)
setLikes(newLikes)
}}>글쓰기</button>
</div>
{
// 중괄호 안에는 js if문 for문 쓸 수가 없어서 대신 삼항 연산자를 씀 : html 중간에 조건문 쓰려면 삼항연산자 쓰세요
modal == true ? <Modal1 title={title} titles={titles} setTitles={setTitles}></Modal1> : null // 불들어오는 기계
}
</div>
)
}
function Modal1(props){
return (
<div className="modal" >
<h4>{ props.titles[props.title] }</h4>
<p>날짜</p>
<p>상세내용</p>
<button>글수정</button>
</div>
)
}
export default App;
응용1. 글에 아무것도 입력안하고 발행버튼 누르는거 막으려면?
유저의 의도치않은 행동을 막는 코드도 많이 짜야 안전한 사이트가 됩니다.
응용2.글을 하나 추가하면 따봉갯수 개별적용하던 것도 이상해질 수 있습니다.
어떻게 해결하면 될까요?
아마 글이 하나 추가되면 따봉기록할 곳도 하나 더 만들어줘야할듯요.
-> 이미 함.
응용3. 날짜데이터는?
state에 글만 저장되어있는데 날짜같은 것도 저장해두고 보여주는 식으로 하면 재밌을 것 같군요.
자바스크립트로 현재 날짜같은 것도 출력해볼 수 있어서 글 발행시 그런 기능을 더해줄 수도 있겠네요.
응용1.
생각보다 간단했다! 입력값의 앞뒤 공백을 제거하는 매서드인 trim()을 사용하여 앞뒤공백을 제거한 후, length()로 길이를 재서 길이가 0보다 클때만 글을 등록하는 함수가 작동하도록 if문을 걸었다.
(참고 공백에 typeof()를 걸어도 string 이 나온다. 타입으로 거르려 했는데 typeof('')도 문자열이었다는 것을 간과함..!!)
<div>
<input onChange={(e)=>{ setContents(e.target.value); console.log(contents)}}></input>
<button onClick={()=>{
// console.log("버튼 :", contents.trim(), contents.trim().length, typeof(contents.trim()))
let inputValue = contents.trim().length
if(inputValue > 0){
let newTitles = [...titles];
let newLikes = [...likes]
newTitles.unshift(contents);
newLikes.unshift(0);
console.log(newTitles)
setTitles(newTitles)
setLikes(newLikes)
}else{ alert("글을 입력해 주세요!") }
}}>글쓰기</button>
</div>
응용3.
귀찮..ㅎ
이건 반복이니까 주말에 시간날 때 해보자.
컴포넌트를 만들 때 class를 이용하여 만들수도 있다.
function으로 컴포넌트를 만들 때 처럼 바깥에다가 기본 틀을 작성하면 된다.
기본형태는 constructor, super, render 3의 함수가 들어간다.
class Modal2 extends React.Component { constructor(){ super( ); } render(){ return ( ) } }
그리고 class 작성 후 App()에 그릴 때 <class이름></class이름>으로 표시한다.
class가 뭔데요 (출처 코딩애플)
class는 변수, 함수 보관하는 통입니다.
extends는 기존 class안에 있던 변수, 함수 복사해주는 문법입니다.
React.Component라는 class안에 있던 변수와 함수들을 복사해야 컴포넌트라고 인정해주기 때문에
class 어쩌구 extends React.Component라고 쓰는 것입니다.
혹시 옛날 문법을 보게되는 일이 생길지도 모르니 적어둠
/* eslint-disable */ // warning message 제거
import './App.css';
import React, { useState } from 'react';
function App() {
return (
<div className="App">
<Modal2></Modal2>
</div>
)
}
class Modal2 extends React.Component {
constructor(props){
super(props);
this.state = {
name: 'Woo',
age: 20
}
}
render(){
return (
<div>안녕{this.state.name}{this.state.age}
<button onClick={()=>{
this.setState({age : 21})
}}>버튼</button>
</div>
)
}
}
export default App;
이번시간은 간단한 내용이기 때문에 글로 빠르게 진행합니다.
....
글로 정리되어있으니까 들어가서 보면 되고, 너무 길어서 못옮기겠다..
고생했어욤 파트 2도 화이팅!!