Section 2. 리액트 State 및 이벤트 다루기

서진·2023년 7월 17일
0

React 완벽가이드

목록 보기
2/15

📍Event Listener 넣는 방법

react에서는 기본 event handler들을 'on'으로 시작하는 props 형태로 component에 전달한다. 모든 event handler는 다음과 같이 function을 전달받아야 한다.
onClick = {clickHandler}

이때 주의할 사항은, onClick에 props로 함수값을 넘길 때는 함수의 이름만 넘기면 된다. onClick = {clickHandler()} 와 같이 실행 ()을 할 필요가 없다! ()를 넣을 경우 버튼이 클릭될 때가 아닌 JSX 코드가 return 될 때 해당 clickHandler가 실행되게 된다.

const clickHandler = () => {
        console.log('Clicked')
    }
...
<button onClick={clickHandler}>Change Title</button>

📍 사용자 입력 Listening = onChange

input 태그에 onChange Listener 넣기

const ExpenseForm = () => {
    const titleChangeHandler = () => {
        console.log('Title changed!'); 
    };

<input type="text" onChange={titleChangeHandler}/>

input에 타이핑을 할 때마다 콘솔에 'Title changed!'가 찍힐것이다.

바닐라 JS를 떠올려보면, event listener들은 자동으로 해당 event 객체를 사용할 수 있도록 해준다. 이를 리액트에서도 당연히 활용 가능하다.

const ExpenseForm = () => {
    const titleChangeHandler = (event) => {
        console.log(event.target.value); 
    };

<input type="text" onChange={titleChangeHandler}/>

event.target : event가 일어나는 DOM 요소
event.target.value : 해당 요소의 value값 (ex. input의 value값)


📍 state를 업데이트 하는 방법

react 에서는 초기에 rendering될 때, component tree를 따라 가장 밑의 component가 실행될 때까지 파일들을 돌면서 화면에 띄운다. 문제는 이러한 과정이 리액트 초기 실행시 딱 한 번만 이루어진다는 것이다.
따라서 component 내에서 이벤트함수를 통해 변수값을 js 변수값을 업데이트 하여 동적으로 적용하려는 경우, 리액트는 초기 한 번만 렌더링을 하기 때문에 업데이트한 값이 화면에 적용되지 않는 문제가 발생한다.

그래서 우리는 react에게 어떤 component의 값이 바뀌었으니 다시 렌더링 해야해~ 라고 알려줄 필요가 있다. 이때 사용하는 것이 useState 이다.

📍 useState

useStatestate값이 변경되면 component를 다시 rendering해서 화면에 띄워주는 react Hook이다.

✅ React Hook의 가장 큰 규칙 중 하나는 component 내부에서만 사용되어야 하고, component 내부의 함수 (nested function) 안에서 사용되어선 안된다는 것이다.

useState는 초기값 설정을 해주어야 한다.
useState는 변수해당 변수에 새로운 값을 update할 수 있는 함수배열에 담아 반환한다.

const [title, setTitle] = useState(props.title);

state를 update하는 함수 (setTitle)를 호출하면, 해당 함수는 parameter로 전달받은 새로운 title 값을 update해주는 것 뿐만 아니라, 해당 state가 들어있는 component를 re-redering 해준다!!!

즉 state가 변하면 component를 다시 redering 해주게 된다.

🧐 state가 변할 때 component가 재실행되는거면 다시 초기값으로 설정되는 거 아니야?

useState는 완전 처음 호출될 때만 state를 초기값으로 setting해준다. re-rendering될 때는 더이상 초기값으로 설정하지 않고, setter함수에 의해 update된 값을 기반으로 가장 최신에 update된 값으로 설정해준다.

하나의 component에서 state 여러개 사용?

둘 중 상황에 맞게 사용하면 된다. 보통은 1번의 방법을 사용한다.

1️⃣ state 여러개 사용하는 방법

    const [enteredTitle, setEnteredTitle] = useState('');
    const [enteredAmount, setEnteredAmount] = useState('');
    const [enteredDate, setEnteredDate] = useState('');

    const titleChangeHandler = (event) => {
        setEnteredTitle(event.target.value); 
    };
    const amountChangeHandler = (event) =>{
        setEnteredAmount(event.target.value);
    }
    const dateChangeHandler = (event) =>{
        setEnteredDate(event.target.value);
    }
    ...

2️⃣ 여러개를 객체에 담아서 state 하나만 사용하는 방법
주의! ) setter함수를 업데이트 하는 경우, 나머지 두 개의 setter값도 설정해주어야 한다. react는 re-rendering할 때 이전의 데이터와 merge하는 것이 아닌, 새로 update된 값으로 value를 대체하는 것이므로, 객체에 들어있는 모든 값들을 update해주어야 한다.

const ExpenseForm = () => {
    const [userInput, setUserInput] = useState({
        enteredTitle: '',
        enteredAmount: '',
        enteredDate: ''
    })

    const titleChangeHandler = (event) => {
        setUserInput({
            ...userInput,
            enteredTitle: event.target.value,
        })
    };
    const amountChangeHandler = (event) =>{
        setUserInput({
            ...userInput,
            enteredAmount: event.target.value,
        })
    }
    const dateChangeHandler = (event) =>{
        setUserInput({
            ...userInput,
            enteredDate: event.target.value,
        })
    }

하지만,, 이런식으로 update하는 것은 기술적으론 문제가 없지만 에러를 일으킬 가능성이 있다. 왜냐하면 React는 state가 업데이트 될 때 재실행을 하는데, 동시에 많은 state값을 update하게 되면 오래된 state나 잘못된 state값을 가져오게 될 수 있기 때문이다. 따라서 아래의 방법을 사용하자!

✅ 이전 state에 의존하는 state값 update

spread 문법 사용하기
🚨 꼭 지켜야 할 규칙!
setter 함수 안에 바로 값을 넣는게 아니라, 함수를 통해서 값을 넣어야 한다. react에서는 자동으로 해당 함수에 prevState를 전달한다. 즉 이전 state를 parameter로 전달받을 수 있다는 것!

const titleChangeHandler = (event) => {
  setUserInput((prevState) => {
    return {...prevState, enteredTitle: event.target.value};
  })
};

📍 form submit = onSubmit

form 태그 안에 있는 type="submit"인 button이 눌린다면, 해당 버튼이 속한 form에서 submit event를 감지한다. 이 event를 우리는 onSubmit event listener로 감지하고 사용할 수 있다.

cf) form이 submit되면 browser의 기본 동작으로 인해 페이지가 새로고침된다. 이를 방지하기 위해선, preventDefault()라는 JS의 내장함수를 사용한다.

버튼을 누르면, input에 입력된 값들을 저장하도록 하고 싶은경우

const submitHandler = (event) => {
  event.preventDefault();
  const expenseData = {
    title: enteredTitle,
    amount: enteredAmount,
    date: new Date(enteredDate)
  }
  // submit된 후 input안의 value값 초기화하기
  setEnteredTitle('');
  setEnteredAmount('');
  setEnteredDate('');
  }

return (
  <div> 
    ...생략

    // submit event handler
    <form onSubmit={submitHandler}>
      ...
      <button type="submit">Add Expense</button>
    </form>
  </div>
)

📍 데이터를 부모에게 전달하는 방법 ⭐️⭐️⭐️

부모 component에서 함수를 선언하고, 자식 component에서 함수를 호출할 때 parameter에 데이터 값을 전달할 수 있다!

1️⃣ 부모에서 함수를 선언
2️⃣ props를 이용하여 자식에 해당 함수 전달

// 부모 : NewExpense.js

// 부모에서 함수를 선언
const NewExpense = () => {
    const saveExpenseDataHandler = (enteredExpenseData) => {
        const expenseData = {
            ...enteredExpenseData,
            id: Math.random().toString()
        };
    } 
 
    return (
        <div className='new-expense'>
      		// props를 이용하여 자식에 해당 함수 전달
            <ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
        </div>
    )
};

3️⃣ 자식에서 해당 함수를 props로 불러와서 호출
4️⃣ 호출할 때 parameter에 부모에게 전달하려는 값을 넣기

// 자식 : ExpenseForm.js
const ExpenseForm = (props) => {
	... 생략
  	const submitHandler = (event) => {
        event.preventDefault();
        const expenseData = {
            title: enteredTitle,
            amount: enteredAmount,
            date: new Date(enteredDate)
        }

        // 자식에서 해당 함수를 props로 불러와서 호출
		// 호출할 때 parameter에 부모에게 전달하려는 값을 넣기
        props.onSaveExpenseData(expenseData);
      
        setEnteredTitle('');
        setEnteredAmount('');
        setEnteredDate('');
    }
    
    return (
      	// 호출
    	<form onSubmit={submitHandler}>
      	... 생략
      	</form>
    )
}

Component tree에서의 데이터 전달 요약

1️⃣ parent -> child
child component에 prop을 통해 전달

2️⃣ child -> parent
parent component에 함수 선언 후 child component의 props로 함수 전달
child component에서 props로 받은 함수를 호출. 호출 시 전달하려는 데이터를 parameter로 넣어서 전달

3️⃣ sibling끼리
data를 generate하는 component가 child일 경우
child -> parent -> other child 로 전달

profile
🫧 ☁️ 🌙 👩🏻•💻 🌿 🐱 🖱 🍟 🚀 ⭐️ 🧸 🍀 💗

1개의 댓글

comment-user-thumbnail
2023년 7월 17일

뛰어난 글이네요, 감사합니다.

답글 달기