모르면 배우면 된다
리액트는 어떤 흐름으로 실행될까? state 이해하기 본문
리액트 state 이해하기
리액트는 어떤 흐름으로 실행되나? state 이해하기
- 컴포넌트는 JSX에 return되는 함수다.
- 초기 렌더링 시 리액트는 JSX의 컴포넌트를 계속해서 훑어 내려가는데, 더이상 불러올 컴포넌트가 없을 때까지 진행한다. 불러오기가 끝나면, JSX를 DOM 명령어로 번역해 화면에 렌더링한다.
- 가장 처음 시작되는 곳은 index.js로, ReactDOM.render(<App/>, document.getElementById(’root’)); 함수 실행이 첫 번째다. App → ExpenseItem → expenses... 컴포넌트를 타고 타고 타고 내려감. (확인 필요)
- 리액트는 초기 렌더링 시 이 모든 것을 실행한다. 이후 전체적인 렌더링은 진행하지 않는다. 즉, html이 어떻게 바뀌어도 화면은 변하지 않는다.
- 따라서 새로 어떤 데이터가 업데이트됐다는 것을 알릴 수단이 필요하다. 그 수단은 state.
핵심 : 리액트는 초기 렌더링 후엔 전체 렌더링을 진행하지 않는다. 부분 렌더링만 진행한다! 렌더링할 곳이 생겼다고 알려주는 게 state의 역할.
useState
- 일반적인 값(value)을 state로 지정해주는 리액트 기능
- 값의 변화가 컴포넌트에 반영이 돼야 함.
- 컴포넌트 함수 안에서만 사용 가능하다.
- Hooks는 이름 앞에 use가 붙는다.
- 변수가 변화하면서, 컴포넌트를 다시 불러온다.
- useState는 배열을 반환한다. 첫 번째는 state의 현재값이고, 두 번쨰는 값을 update하는 함수다. const에 배열을 먼저 선언하고, useState가 만들어낸 배열을 할당하는 방식이다.
선언
const [title, setTitle] = useState(props.title);
const [값, 값을 변경하는 함수] = useState(초깃값);
사용 : setTitle('') 안에 title에 할당하고자 하는 값을 넣는다.
setTitle('update'); //
- 왜 이런식의 작업을? → state로 할당하면, React는 해당 state를 메모리에 할당한다. 그리고 상태 업데이트 함수를 불러올 때 변수는 새로운 값만 받는 게 아니라, 컴포넌트 함수까지 재실행하게 된다. 왜냐하면, 우리가 새로운 값을 컴포넌트에 할당하고 싶다고 react에 전달하기 때문이다.
- 이에 리액트는 변화가 감지된 곳에 컴포넌트를 다시 실행한다.
변화가 필요한 데이터가 있고, 변화 즉시 화면에 나타내주고 싶을 때 state를 사용한다.
state는 컴포넌트 단위로 적용된다
컴포넌트에 state 변경 함수를 사용한다.
해당 컴포넌트를 재사용한다.
한 컴포넌트에서 state를 변경한다고 해서, 다른 컴포넌트의 state까지 변경되지 않는다.
즉, 상태는 컴포넌트 인스턴스 단위로 나뉘어져 있다.
useState에서 왜 const를 사용했을까?
function ExpenseItem(props) {
const [title, setTitle] = useStaste(props.title);
console.log('hi'); 여기도 다시 실행된다.
const clickHandler = () => {
setTitle('update');
이 메소드를 통해 title의 state가 변화했을 때, ExpenseItem 컴포넌트 자체가 재실행된다.
그러면서 react는 매번 최신 상태의 state를 받게 된다.
}
return (
<Card className='expense-item'>
<ExpenseDate
date={props.date}
></ExpenseDate>
<div className="expense-item__description">
<h2>{props.title}</h2>
<div className="expense-item__price">{props.amount}</div>
</div>
</Card>
);
}
폼 만들기
return (
**<form>**
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input type="text"></input>
</div>
<div className="new-expense__actions">
**<button type="submit">Add Expense</button>**
</div>
**</form>**
);
};
입력값을 모두 탐지하는 리스너 : onChage
모든 입력 type에 적용할 수 있다. 드롭다운, input 다..
state를 한 useState로 관리하기
이렇게 쓴느 거 별로인 거 같음...
const [userInput, setUserInput] = useState({
enteredTitle: '',
enteredAmount:'',
enteredDate:'',
});
왜냐하면
const dateChangeHander = (event) => {
setUserInput(
...userInput,
enteredDate: event.target.value;
};
값을 set 해줄 때마다 다른 프로퍼티의 값도 챙겨줘야 하기 때문에...를 써야함. 이전 state에 의존해, 값을 복사하고, 새로운 값을 넣는다.
다만, 좋은 사례는 아님. 이전 state의 값을 참고해 새로 업데이트할 경우, 아래와 같이 써야 함.
setUserInput((**prevState**)=>{
return {...prevState, enteredTitle: event.target.value };
});
또다른 예시
setExpenses((prevExpenses)=>{
return [expense, ...prevExpenses];
});
리액트가 상태 업데이트를 계획한 경우를 고려해야 한다. 동시에 많은 상태 업데이트를 계획한다면 잘못된 이전 state에 접근할 수 있다. 하지만 위 코드는 리액트 inner function으로 prevState를 정확하게 받아올 수 있다. 이전 상태에 따라 현재 상태를 업데이트해야 한다면 이 폼을 써야 한다.
form 데이터 보내기
onClick으로 이벤트 처리할 수도 있긴 하지만. onSubmit={}을 form에 넣어주기.
페이지 리로드가 되는 기능이 onSubmit에 있는데, 그 동작을 원하지 않는 경우, event 객체에서 제어할 수 있다. event.preventDefault(); 자바스크립트의 기본 동작이다. 리퀘스트를 보내지 않기 때문에 리로드가 되지도 않는다.
const submitHandler =(event)=>{
**event.preventDefault();**
const expenseData = {
title: enteredTitle,
amount : enteredAmount,
date : new Date(enteredDate),
};
console.log(expenseData);
};
return (
<form onSubmit={submitHandler}>
양방향 바인딩
- input에 value를 useState의 value로 설정
- onSubmit 에서 value의 set을 초기화하는 이벤트를 설정
결과 : submit 이벤트 실행 시 input 값이 useState의 초기값으로 설정됨
const submitHandler =(event)=>{
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount : enteredAmount,
date : new Date(enteredDate),
};
console.log(expenseData);
**setEnteredTitle('');
setEnteredAmount('');
setEnteredDate('');**
};
**<form onSubmit={submitHandler}>**
<div className="new-expense__controls">
<div className="new-expense__control">
<label>title</label>
**<input type="text" value={enteredTitle} onChange={titleChangeHandler}></input>**
</div>
자식 컴포넌트에서 부모 컴포넌트로 데이터 넘기기
props는 부모와 자식 간에서만 소통 가능하다. 조부모로 뛰어넘을수 없음
보통 props에서 함수가 올 것임을 의미할 때는 on~로 통상적으로 이름을 지음
부모 컴포넌트에서 자식 컴포넌트 데이터 받아오기
const NewExpense = () =>{
//자식 컴포넌트 ExpenseForm에서 데이터 받아오기
**const saveExpenseDataHandler =(enteredExpenseData)=>{ //Form에서 받아온 데이터**
const expenseData ={
**...enteredExpenseData,**
id: Math.random.toString(), //form Data에 id 프로퍼티 추가하기
};
console.log(expenseData);
}
return (
<div className='new-expense'>
**<ExpenseForm onSaveExpenseData={saveExpenseDataHandler}></ExpenseForm>
</div>**
);
};
자식 컴포넌트에서 부모 컴포넌트의 함수 사용하기
const submitHandler =(event)=>{
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount : enteredAmount,
date : new Date(enteredDate),
};
**props.onSaveExpenseData(expenseData);** //부모 컴포넌트에서 받아온 함수 실행. 함수명은 props의 key다.
setEnteredTitle('');
setEnteredAmount('');
setEnteredDate('');
};
형제 컴포넌트끼리 데이터 주고받기
리액트 컴포넌트는 형제 간 데이터를 이동시키지 못한다. 자식과 부모 사이만 가능하다. 형제끼리 소통을 시키고 싶으면 형제 A→부모→형제B 식으로 해야 한다. 절연한 형제....
단, 데이터를 갖고 있는 컴포넌트와, 데이터를 필요로하는 컴포넌트의 위치를 같도록 지정해야 한다.
부모 컴포넌트에서 자식A 컴포넌트의 데이터를 받아올 함수 만들기
const addExpenseHandler = expense =>{
//NewExpense 컴포넌트에서 새로 추가한 expense데이터를 받아오는 함수.
//이 함수를 Expenses 컴포넌트에 보내서 활용한다.
//형제 컴포넌트 간 데이터 이동의 전형적인 예시.
//형제끼리 다이렉트로 소통하지 못한다.
//이 데이터는 Form.js에서 NewExpense, App까지 끌어올려진 뒤, Expense로 넘어감.
//사용자가 직접 생성하는 새로운 Expenses 데이터이기 때문입니다~
console.log('In App.js');
console.log(expense);
};
그 함수를 props로 자식에게 넘기기
return (
<div className="App">
<NewExpense **onAddExpense**={addExpenseHandler}/>
자식 A 컴포넌트에서 props.함수로 데이터 받아오기
const saveExpenseDataHandler =(enteredExpenseData)=>{ //Form에서 받아온 데이터
const expenseData ={
...enteredExpenseData,
id: Math.random.toString(), //form Data에 id 프로퍼티 추가하기
};
console.log(expenseData);
**props.onAddExpense(expenseData);**
}
부모 컴포넌트에서 받아온 데이터를 자식B에게 props로 넘기기
'FRONT' 카테고리의 다른 글
CSS 스터디 1주차 : 셀렉터, 콤비네이터, 가상셀렉터 (0) | 2022.08.30 |
---|---|
자주 쓰는 react Hooks 정리 : useEffect, useMemo (0) | 2022.05.12 |
리액트 동적으로 컴포넌트 만들기! (0) | 2022.02.26 |
리액트의 기본, JSX 문법과 컴포넌트 만들기 (0) | 2022.02.06 |