Data binding
JSX에서 데이터바인딩하기
데이터 바인딩?
자바스크립트 데이터를 HTML에 꽂아넣는 작업을 뜻한다.
> 변수를 만들어서 넣기
{ } 중괄호에 넣어서 온갖곳에 변수 집어넣기 가능하다.
import React, { useState } from "react";
let data = "안녕";
function app() {
return (
<div className="App">
<div className="black-nav">
<div>{data}</div>
</div>
</div>
);
}
export default app;
> html에 스타일 직접 넣기
{ } 중괄호에 넣어서 { 속성명 : '속성값'} 속성명은 camelcase로 입력해야된다.
<div style={{ color: "red" }}>{data}</div>
보기싫으니까 변수명에 넣어서 사용하거나 css파일에서 하도록 하자.
let data = { color : 'red'}
<div style={data}>
State / useState
중요한 데이터는 state로 저장한다.
state를 쓰는이유?
: 변수가 변경될 때 자동으로 관련된 html의 재렌더링을 원할때 사용한다.
리엑트는 state가 수정이 일어나면 state가 포함된 html을 자동으로 재렌더링 해준다. (새로고침 없이 스무스하게~)
: state는 instance 기반 단위로 나누어져있다.
ES6 destructuring 문법
> array안에 있는 데이터를 변수에 저장한다.
import React, {useState} from 'react';
let [a,b] = useState('저장', '저장변경')
// a= 저장, b=저장변경
state에는 array, object 아무거나 다 넣을 수 있다.
이때도 역시 중괄호를 사용한다.
let [ name, namechange ] = useState(기본값);
<div> { name } </div>
useState로 state를 변경한다.
let [ name, namechange ] = useState(기본값);
버튼기능은 onClick으로 만든다.
<div onClick={실행할 함수}>
//함수
<div onClick={어디서만들어놓은함수명}>
<div onClick={ () => {실행할 코드} }>
> 2가지로 나타낼 수 있다.
1. 다음상태를 추가
2. 업데이트할 상태추가(함수로)
const [number, setNumber] = useState(0);
//다음상태
const Decrease = () => {
setNumber(number - 1);
};
//상태 업데이트
const Increase = () => {
setNumber((prevNumber) => prevNumber + 1);
};
값을 변경할때는 지정된 변경함수를 쓰는게 포인트다.
onClick = { ( ) => { } } 이벤트 핸들러를 달아어 사용한다.
중괄호안에 { state변경함수 (대체할데이터) } 를 넣어준다.
소괄호내에있는 데이터로 완전히 대체된다. (소괄호안에는 깔끔하게 변경할 값만 넣어줘야한다.)
function app() {
let [like, likeChange] = useState(0);
//like=state , likeChange=state변경함수
return (
<h3 onClick={() => {likeChange(like + 1);}}>
{post} <span>🙉</span> {like}
</h3>
state는 등호나 직접수정이 불가능해서 state변경함수를 사용해야 한다.
state를 아예 대치해서 변경해야된다는 뜻이다.
function 제목바꾸기 () {
state변경함수(대치,변경)
}
1. 쉽게 바꾸려면 바꿔치기할 새로운 array를 통째로 넣는다.
function 제목바꾸기 () {
state변경함수(['제목변경', '제목2', '제목3'])
}
2. state 변경함수 만드는 방법
function 제목바꾸기고급버전 () {
let newArray = [...글제목]; //state복사본을 새변수에 저장한다.
newArray[0] = '제목변경' //복사본을 조작한다.
글제목변경(newArray); //변경된거를 변경함수에 집어넣는다.
}
<div onClick={제목바꾸기고급버전}> 버튼 </button>
> array, object자료형에서 = 등호로 복사하면 별개의 자료형이 아니고 값을 공유하게된다. (javascript reference data type)
따라서, shallow/deep copy를 해줘야한다.
let 새로운array = [...원본array];
...은 뭐임?
1. 괄호벗길때 사용한다.
2. 괄호벗기고 완전 독립적인 array를 만들어준다.
복사본 매번 반듬?
: 복사본은 reference 자료형들만 (array, object 요런거)
문자, 숫자, true/false 이런건 직접수정하면됨
Input : 사용자가 입력한 글을 변수에 저장하기
> input값 저장흐름 ( <textarea>, <select> 태그들도 동일 )
1. 값 저장할 빈 state생성
2. input 태그에 onChange 이벤트 핸들러를 단다. ( onChange 입력할 때 특정함수 동작시킬 때 사용한다.)
e.target.value ?
( js 문법. e.target = '지금 이벤트가 동작하는 html요소' / .value = 'input에 입력한 값' )
function App () {
let [입력값, 입력값변경] = useState(' ');
return (
<div>
<input onChange = { (e) => { 입력값변경(e.target.value) }} />
</div>
)
}
//확인할 때 콘솔창 찍어보기 {(e) => {console.log(e.target.value)}
> 여러개의 input 상태관리하기 ( 객체에서 새 항목 추가 )
export const InputSample2 = () => {
const [input, setInput] = useState({
name: '',
nickname: '',
});
const { name, nickname } = input;
const onChange = (e) => {
const { name, value } = e.target;
setInput({
...input,
[name]: value,
});
};
const onReset = (e) => {
setInput({
name: '',
nickname: '',
});
nameInput.current.focus();
};
const nameInput = useRef();
return (
<>
<input
placeholder="name"
onChange={onChange}
name="name"
value={name}
ref={nameInput}
/>
<input
placeholder="nickname"
onChange={onChange}
name="nickname"
value={nickname}
/>
<button onClick={onReset}>Reset</button>
</>
);
};
> 배열에 항목 추가하기 (클릭하면 게시물 추가시키기)
- push, splice, sort 등의 함수로 기존의 state를 변경하려면 state를 복사해서 변경해준다.
- 배열의 불변성을 유지하면서 항목을 추가하려면
1. spread 연산자로 복사한 후 새항목을 추가한다.
fucntion app() {
let [글제목, 글제목변경] = useState(['title1', 'title2', 'title3']);
let [입력값, 입력값변경] = useState('');
//글제목변경하는 함수만들어서 button에 넣어준다.
funtion pushName() {
let newArray = [...글제목];
newArray.unshift(입력값);
글제목변경(newArray);
}
return (
<div>
<input onChange={ (e) => {입력값변경(e.target.value}} />
<button onClick={ ()=> {pushname} >저장</button>
)
각각 다른 모달창 제작
> 버튼마다 다른 모달창제목 뜨도록 제작하기
1. 몇번째 제목 눌렀는지 state로 저장한다. (기본값0)
2. state가 0일때는 0제목, 1일때는 1제목 출력해준다. (state달라지면 UI도 달라지게 만든다.)
> UI만드는 법은 다 똑같다.
1. state하나 만든다.
2. state가 ~한 상태일때 이런 UI보여줘~
(3. 추가: 버튼 누르거나할 땐 state를 이렇게 바꿔줘 )
의식의 흐름
// 처음에 버튼3개로 시작해본다.
function App() {
let [글제목, 글제목변경] = useState(0);
let [누른제목, 누른제목변경] = useState(0);
return (
<div>
<button onClick={() => { 누른제목변경(0) }}버튼1</button>
<button onClick={() => { 누른제목변경(1) }}버튼2</button>
<button onClick={() => { 누른제목변경(2) }}버튼3</button>
...
<Modal 글제목={글제목} 누른제목={누른제목} />
</dvi>
)};
function Modal (props) {
return (
<p> 제목 {props.글제목[props.누른제목] } </p>
...
글의 제목누르면 state가 변경되게 만들어본다.
누른제목변경 : 반복문 돌때 차례로 0,1,2...되는 함수 index (파라미터에 두번째로 추가한다)
function App() {
return (
<div>
...
{글제목.map (function(a, i) {
return (
<h3 onClick = { () => { 누른제목변경(i) }}> {a}
//
Component / map
html을 한단어로 치환하는 Component
> 어떤 html들을 component로?
: 반복출현하는 애들을 재사용 하고 싶을 때
긴코드를 축약하고 싶을 때
내용이 자주 변경되는 애들
기능별로 나누고 싶은 애들
> 단점 ?
: props를 이용해서 state를 component까지 전해줘야 사용이 가능하다.
> 만드는 방법?
1. function을 이용해서 함수를 하나 만들어준다.
2. 그 함수 안에 return () 안에 원하는 HTML을 담는다.
3. 원하는 곳에서 <함수명 />이라고 사용했을 때 아까 축약한 HTML이 등장한다.
예제) 클릭하면 보이는 모달창 만들기
: 리엑트 중괄호내에서 if문같은거 못쓰니까 삼항연산자를 이용한다.
( 조건식 ? 조건식참일때실행코드 : 거짓일때실행코드 )
텅빈 html을 나타낼때는 null을 쓴다.
> 구현 과정
0. Modal Component를 만든다.
1. 모달창 보이는 / 보이지않는 상태를 저장할 state를 만든다.
2. 삼항연산자 이용 && 사용해서 true일때 모달창 보이게 해준다. (false일땐 null)
3. 열기버튼 눌렀을 때 모달창 보이게 해준다. (클릭 on off 연속 > !modal )
* 내용이 달라질때는 삼항연산자 사용
null 값 나타내고 싶을 땐 and 연산자 사용
//1
let [modal, modalChange] = useState(false);
//2
{ modal === true ? <Modal /> : null }
{ modal === true && <Modal /> }
//3
<button onClick = { () => {modalChange(!modal) } />
map함수
: 기존 array 변형시켜서 새로운 array를 만들어준다.
중괄호 { } 안에는 변수, 함수 이런것만 입력가능하다.
for문 이런거 못쓰니까 map사용한다. ( array에 붙일 수 있는 일종의 내장함수 )
let array = [ 2,3,4 ];
{
array.map (콜백함수()=>{
return ~반복시킬 html~
});
//array의 자료갯수만큼 실행된다.
> map은 원본을 변형시키지않는다.
그러므로 새로운 array에 담아준다.
> 콜백함수에 파라미터를 하나 추가하면 array안의 데이터들을 하나씩 출력해준다.
//1.원하는 자료에 map을 붙여준다.
//2.return안에 반복원하는 html을 넣어준다.
let newArray = 어레이.map (function(a) {
return a*10
});
* key = {i}
키값 지정해줘야 에러안뜬다!
키값이 있어야 어떤 element를 지칭하고 있는지 정확히 인식하여, 새로운 항목이 추가되거나 제거될때 효율적으로 업데이트(render)가 된다.
id 값이 없으면 index로 키값을 지정할 수 있지만, 비효율적으로 업데이트 된다.
> map함수 예제
//부모 컴포넌트
//shoes데이터의 i번째 데잍터를 받아오고 싶을 때, {shoes[i]} 혹은 {a}를 해준다.
function App() {
let [shoes, shoes변경] = useState(data);
return (
{shoes.map((a, i) => {
return <Shoes shoes={shoes[i]} i={i} key={i} />;
})}
/* 상품이미지들 데이터바인딩하기
글자 중간에 변수를 넣고싶으면 '문자' + 변수 + '문자' */
import React from "react";
//props로 부모의 데이터 받아온다.
function Shoes(props) {
return (
<div className="container">
<div className="row">
<div className="col-md-4">
<img
src={"https://codingapple1.github.io/shop/shoes" + (props.i + 1) + ".jpg"}
width="100%"
alt={props.Reacti}
/>
<h5>{props.shoes.title}</h5>
<p>
{props.shoes.content}&{props.shoes.price}
</p>
</div>
</div>
</div>
);
}
export default Shoes;
Props
Props : 자식이 부모의 state를 가져다 쓰고 싶을 때
부모의 state변수는 props로 자식까지 state를 전송해줘야 사용가능하다.
1. <자식componame 작명={stateName} />
2. 자식 component 선언하는 function안에 파라미터 생성한다.
//부모
//{변수명} or '텍스트' 둘다가능하다.
<Modal 전송할이름={state명} />
//props여러개 가능하다.
<Modal 이런거="이런거" 저런거="저런거" />
//자식
function Modal (props) { //props에 모든 props데이터 담겨있다.
return (
<h2> {props.글제목[0]} </h2> //props에서 필요한 데이터만 사용한다.
props쓰기 귀찮는데 자식component에다가 바로 꽂아넣으면 안되나?
: 데이터는 항상 위에서 아래로 흘러야한다.
옆 자식이 데이터필요하면 부모로 올려보냈다가 다시 옆자식에게 줘야하잖아..데이터 역방향으로 전달시키면 props보다 더귀찮아진다..
그러니까 state만들때는 state를 필요로하는 컴포넌트 중 가장 최상위 컴포넌트에 보관해야한다.
Children Props
단순히 shell 역할만하는 componet를 만들고 싶을 때
ex) box의 css
Expenseitem 컴포넌트에서 card컴포넌트의 css를 사용하고 싶을 때 Card컴포넌트로 감싸주면
<Card></Card> 내부에있는 요소들의 classname props로 넘겨줄 수 있다.
function Expenseitem(props) {
return (
<Card className="expense-item">
<ExpenseDate date={props.date} />
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">${props.amount}</div>
</div>
</Card>
);
}
Card.js 컴포넌트 생성
1. {props.children}
2. Card 컴포넌트의 classname 'card '와 props로 받은 Expenseitem에서 Card안에 요소들의 classname을 결합하여 css를 씌워줄 수 있다. ( "card " 에서 띄어쓰기 해주기! )
import "./Card.css";
function Card(props) {
const classese = "card " + props.className;
return <div className={classese}>{props.children}</div>;
}
export default Card;
.card {
border-radius: 12px;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
}
조건부 렌더링
React에서 조건부 렌더링 3가지 방법
[기존]
{filteredExpenses.map((expense) => (
<ExpenseItem
key={expense.id}
title={expense.title}
amount={expense.amount}
date={expense.date}
/>
))}
1. 삼항연산자
<ExpensesFilter dataSave={dataSave} selected={filteredYear} />
{filteredExpenses.length === 0 ? (
<p>No expense found.</p>
) : (
filteredExpenses.map((expense) => (
<ExpenseItem
key={expense.id}
title={expense.title}
amount={expense.amount}
date={expense.date}
/>
))
)}
2. &&
{filteredExpenses.length === 0 && <p>No expense found.</p>}
{filteredExpenses.length > 0 &&
filteredExpenses.map((expense) => (
<ExpenseItem
key={expense.id}
title={expense.title}
amount={expense.amount}
date={expense.date}
/>
))}
3.변수에 추가
return 전에 변수에 로직 추가해놓고 JSX코드에서 변수만 깔끔하게 넣어준다.
let expensesContent = <p>No expense found.</p>;
if (filteredExpenses.length > 0) {
expensesContent = filteredExpenses.map((expense) => (
<ExpenseItem
key={expense.id}
title={expense.title}
amount={expense.amount}
date={expense.date}
/>
));
}
return (
{expensesContent}
)
삼항연산자 vs &&
//삼항연산자
{isEditing ? (
<button onClick={startEditingHandler}>Add new Expense</button>
) : (
<ExpenseForm
onSaveExpenseData={saveExpenseDataHandler}
onNewForm={startEditingHandler}
/>
)}
//&&연산자
{isEditing && (
<button onClick={startEditingHandler}>Add new Expense</button>
)}
{!isEditing && (
<ExpenseForm
onSaveExpenseData={saveExpenseDataHandler}
onNewForm={startEditingHandler}
/>
)}
'• React' 카테고리의 다른 글
[React] styled-components / SASS / CSS Module (0) | 2021.10.24 |
---|---|
[React] Router (0) | 2021.10.24 |
[REACT] State / Event (0) | 2021.09.04 |
[REACT] Component / Props / Mock data (0) | 2021.09.01 |
[REACT] JSX / Component (0) | 2021.08.30 |