React/React 2023

React 002-1 State,Event

salalsksjwnn 2023. 1. 2. 11:37
728x90

그동안 정적인 어플리케이션은 만들었다 이제는 상호작용하는 동적인 애플리케이션을 만들어보자!

이벤트 리스닝, 이벤트 핸들러

모든 on props 즉 모든 이벤트 핸들러 props는 값으로 함수가 필요하고 이런 on props에 전달된 함수는 이벤트가 발생했을 때 실행되너야 한다.

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

이벤트 이름을 지을 때 이벤트에서 트리거 퇴고 핸들러로 끝나게 만드는 게 좋다. clickHandler 

State

state는 리액트에만 특화된 것은 아니지만 리액트에서 아주 중요한 개념이다.

useState를 임포트 해준다.

useState는 가장 중요한 리액트 훅 중의 하나 (훅들은 이름이 use로 시작해서 쉽게 알아낼 수 있다) 컴포넌트 함수 안에서 호출되어야 한다.

  • useState는 항상 두 개의 요소가 있는 배열을 반환하는데 첫번째 요소[title]는 현재 상태값이고 두번짜 요소[setTitle]는 그것을 업데이트 하는 함수이다.
const ExpenseItem=(props)=> {
  const [title, setTitle] = useState(props.title);

  const clickHandler=()=>{
    setTitle('Updated!');
    console.log(title);
    /**
     * 이 함수를 호출하는 것은 어떤 변수에 새로운 값을 할당하는 것이 아니라 이 특별한 변수로 시작한다.
     * 이 특별한 변수는 새로운 값만 받는 것이 아니라 state를 업데이트 하는 함수를 호출하게 되고 
     * useState로 상태를 초기화 했던 곳에서 다시 실행될 것이다.
     * state가 변할 때 이 컴포넌트 함수를 다시 호출하고 싶으면 이 state를 업데이트하는 함수를 호출하면된다.
     */ 
  }

useState는 몇몇 state를 등록한다. useState는 특정 컴포넌트의 인스턴스를 위해 state를 등록한다. 같은 기능을 가진 컴포넌트를 수정해도 그 하나에만 영향을 주지 다른 컴포넌트에는 영향을 주지 않는다.

const를 사용하는 이유? 

state를 업데이트하는 함수를 호출하고 구체적은 값은 리액트에 의해 관리된다. 그래서 ustState를 호출해서 리액트에게 어떤 값ㅅ을 관리해야한다고 선언하는 것 그리고 stat가 업데이트 되면  재 실행 되는 것

 

쉽게 말해

나 "야 리액트, 내가 저번에 관리하라고 했던 최신 제목상태를 줘"

라고 명령하고

리액트는ustState가 반환하는 배열 [title, setTitle] 에서 가장 최신의 상태를 우리에게 제공하는 것이다.

 

useState 가 작동하는 방식

  1. useState를 사용해서 상태를 등록한다.
  2. 항상[현재 상태값, 업데이트하는함수] 두개의 값을 갖는다
  3. state가 변할 때 마다 업데이트 함수를 호출한다.
  4. JSX코드에서 출력하기 위해 상태값을 사용하고 싶을 때 마다 첫번째 요소 [현재상태값]을 사용한다.
  5. 리액트가 작업을 한다.
    1. 상태가 변할때마다 컴포넌트형 함수를 다시 실행하고
    2. JSX코드를 다시 평가한다.

state는 응용프로그램에게 반응성을 추가하기 때문에 아주 중요하다.

Input추가하기

출력하는 컴포넌트들을 관리하는 폴더들을 만들었는데 이제는 입력하는 컴포넌트가 들어갈 폴더를 만들어보자

대강의 폼을 준 ExpenseForm.js

import React from'react';
const ExpenseForm=()=>{
  return <form action="">
    <div className="new-expense__controls">
      <div className="new-expense__control">
        <label>Title</label>
        <input type="text" />
      </div>
      <div className="new-expense__control">
        <label>Amount</label>
        <input type="number" min="0.01" stop="0.01" />
      </div>
      <div className="new-expense__control">
        <label>Date</label>
        <input type="date" min="2019-01-01" max="2022-12-31" />
      </div>
    </div>
    <div className="new-expense__actions">
      <button type="submit">Add Expense</button>
    </div>
  </form>
};

export default ExpenseForm;

NewExpense.js에서 ExpenseForm.js를 임포트해주고 App.js에다 NeweExpense.js까지 임포트 해준다.

//NewExpense.js

import React from'react';
import ExpenseForm from './ExpenseForm';
import './NewExpense.css'
const NewExpense=()=>{
  return <div className="new-expense">
    <ExpenseForm />
  </div>
};

export default NewExpense;

------------------------------------

//App.js

import React from'react';
import ReactDOM from'react-dom';

import Expenses from "./components/Expenses/Expenses";
import NewExpense from './components/NewExpense/NewExpense';
...
  return (
    <div>
      <NewExpense />
      <Expenses item={expenses} />
    </div>
  );
}

export default App;

나왔군요 

아 css 임포트 깜빡했다
css임포트까지 해주면 잘 나온다

사용자 입력 리스닝

이제 사용자가 입력하는 데이터를 모아주자. 리스너를 추가해서 사용자의 요청을 들을 수 있게 해주기

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

  };

  return <form action="">
    <div className="new-expense__controls">
      <div className="new-expense__control">
        <label>Title</label>
        <input type="text" onChange={titleChangeHandler} /> 
      </div>
      ...
  • titleChangeHandler라는 함수를 만들어주고 인자로 event를 잡아주면 어떤일이 일어나는지 알 수 있다.
  • 확인을 위해 콘솔로 value가 어떻게 찍히는지 보고
  • JSX input에 onChange를 주고 방금 만든 titleChangeHandler로 인자를 준다.
  • input에 변화가 생길때마다 콘솔에 변화가 된 값이 찍히게 된다.

여러 State 다루기

이제 그거로 무엇을 할 수 있을까? 나중에 폼이 제출되었을 때 입력값들을 모아 객체에 전달해야한다.

import React, {useState} from'react';
import './ExpenseForm.css'

const ExpenseForm=()=>{
  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);
  };

...

다른 방식으로 State사용하기

useState를 한번만 뿌려주는 방법

import React, {useState} from'react';
import './ExpenseForm.css'

const ExpenseForm=()=>{
  // const [enteredTitle, setEnteredTitle]=useState('');
  // const [enteredAmount, setEnteredAmount]=useState('');
  // const [enteredDate, setEnteredDate]=useState('');
  const[userInput,setUserInput] = useState({
    enteredTitle:'',
    enteredAmount:'',
    enteredDate:''
  });

  const titleChangeHandler=(event)=>{
    // setEnteredTitle(event.target.value);
    setUserInput({
      ...userInput, //다른 데이터들이 사라지지 않도록
      enteredTitle:event.target.value,
    })
  };
  const amountChangeHandler=(event)=>{
    setUserInput({
      ...userInput, //다른 데이터들이 사라지지 않도록
      enteredAmount:event.target.value,
    })
  };
  const dateChangeHandler=(event)=>{
    setUserInput({
      ...userInput, //다른 데이터들이 사라지지 않도록
      enteredDate:event.target.value,
    })
  };

근데 특정 상태에서 실패할 수 도 있는 방법이라고 한다.  이전 state에 의존하는게 아니라 상태를 업데이트 하는 폼을 사용해주어야한다. 항상 업데이트가 될 수 있다는 것을 인지하게 해주는 것

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

근데 여기선 일단 첫 방법으로 진행할거다 돌아가 주자

폼 제출

버튼이 눌렸을 때 이 state들을 객체로 결합하고 싶다.

...
const submitHandler = (event) => { //이벤트 객체를 다시 얻는다.
    event.preventDefault();//내장 자바스크립트 기본요청이 전달되는걸 막을 수 있다.
    const expenseData = {
      title:enteredTitle,
      amount:enteredAmount,
      date:new Date(enteredDate),
    };
    console.log(expenseData);

  };

  return (
    <form onSubmit={submitHandler}>
    ...

양방향 바인딩 추가

입력된 것들을 어떻게 없앨 수 있지?

state를 사용하면 양방향 바인딩을 할 수 있다. 변경되는 입력값만 바꾸는 것이 아니라 입력에 새로운 값을 다시 전달 할 수 도 있는 것 이다. 프로그램에 따라 입력값을 재 설정 할 수 있는 것

간단하게 value를 설정해 주면 된다.

무한루프 같지만 정상작동한다.

   ...
   console.log(expenseData);
    setEnteredTitle('');  //폼이 전송되고 입력했던 걸 오버라이드해서 깨끗하게 정리
    setEnteredAmount(''); 
    setEnteredDate(''); 
  };

  return (
  ...

다시 빈칸으로 돌아온다.

자식 대 부모 컴포넌트 통신(상향식)

이제ExpenseForm에는 정확하게는 App.js에는 데이터가 필요없다 데이터를 받아주기만 하면 되니까..

ExpenseForm에서 받은 데이터를 어떻게 App.js로 보낼 수 있을까?

 

  • ExpenseForm에서 NewExpense로 보내기
    • NewExpense에 속성을 추가해 준다.
    • 맞춰서 함수도 작성한다.
import React from "react";
import ExpenseForm from "./ExpenseForm";
import "./NewExpense.css";
const NewExpense = () => {
const onSaveExpenseData = (enteredExpenseData)=>{
  const expenseData={
    ...enteredExpenseData,
    id:Math.random().toString()
  };
  console.log(expenseData);
};
  
  return (
    <div className="new-expense">
      <ExpenseForm onSaveExpenseData={onSaveExpenseData} />
    </div>
  );
};

export default NewExpense;
  • NewExpense에서 App.js로 보내기
...
const addExpenseHandler = expense => {
    console.log('In App.js');
    console.log(expense);
  }

  return (
    <div>
      <NewExpense onAddExpense={addExpenseHandler} />
      <Expenses item={expenses} />
    </div>
  );
}
...

 

 

728x90