[React] 조건에 따라 폼태그 보여주고 숨기기 (feat. state, 클릭 이벤트)

seung·2022년 5월 24일
1

유데미의 리액트 강의를 듣던 중에 버튼을 클릭하면 조건에 따라 폼 태그를 보여주거나 보여주지 않거나 하는 기능을 만들어 보는 것을 과제로 받았다.


form 숨겨진 상태

위와 같이 처음에는 Add New Expense 버튼이 하나만 보이고,

해당 버튼을 클릭했을 때 내용을 입력할 수 있는 폼을 보여준다.


form 보여진 상태

Add New Expense 버튼이 사라지고, 폼이 보여진다.

폼 내에서 Cancel 버튼을 클릭하면 초기화면과 같은 Add New Expense 버튼만 보여준다.

폼에 내용을 입력하고 Add Expense 버튼을 클릭하면 expense가 추가된 후 Add New Expense 버튼만 보여준다.


방법

NewExpense 컴포넌트 내에서 expense 추가해주는 상황인지 아닌지는 state를 추가하여 확인했다.

isEditing 이라는 state를 추가하여 해당 상태값을 이용해 반환값을 다르게 했다.

  1. isEditing이 false 이면 Add New Expense 버튼 반환
  2. isEditing이 true 이면 form 반환

로직

  1. 초기상태는 isEditing을 false로 설정한다.
    1. 처음 화면은 Add New Expense 버튼 하나만 렌더링 된다.
  2. Add New Expense 버튼 클릭하면 isEditing 을 true로 변경한다. (startEditing)
    1. form 반환하여 form 렌더링 된다.
  3. form 컴포넌트 내부에 Cancel 버튼 추가하고, 해당 버튼 클릭하면 부모 컴포넌트(NewExpense)의 stopEditing 함수 호출한다. (isEditing → false)

NewExpense (부모 컴포넌트)

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

const NewExpense = (props) => {
  const [isEditing, setIsEditing] = useState(false);

  const saveExpenseData = (enteredExpenseData) => {
    const expenseData = {
      id: Math.random().toString(),
      ...enteredExpenseData,
    };
    props.onAddExpense(expenseData);
    setIsEditing(false);
  };

  const startEditingHandler = () => {
    setIsEditing(true);
  };

  const stopEditingHandler = () => {
    setIsEditing(false);
  };

  // 유의할 점은 함수를 실행하지 않고 함수 자체를 ExpenseForm 컴포넌트에 전달했다는 것이다.
  return (
    <div className="new-expense">
      {!isEditing && (
        <button onClick={startEditingHandler}>Add New Expense</button>
      )}
      {isEditing && (
        <ExpenseForm
          onSaveExpenseData={saveExpenseData}
          stopEditing={stopEditingHandler}
        />
      )}
    </div>
  );
};

export default NewExpense;

자식 컴포넌트 (ExpenseForm)

import React, { useState } from 'react';

import './ExpenseForm.css';

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

  const submitHandler = (event) => {
    event.preventDefault();

    const expenseData = {
      title: enteredTitle,
      amount: +enteredAmount,
      date: new Date(enteredDate),
    };

    props.onSaveExpenseData(expenseData);

    setEnteredTitle('');
    setEnteredAmount('');
    setEnteredDate('');
  };

  return (
    <form onSubmit={submitHandler}>
      <div className="new-expense__controls">
        <div className="new-expense__control">
          <label>Title</label>
          <input
            type="text"
            value={enteredTitle}
            onChange={titleChangeHandler}
          />
        </div>
        <div className="new-expense__control">
          <label>Amount</label>
          <input
            type="number"
            min="0.01"
            step="0.01"
            value={enteredAmount}
            onChange={amountChangeHandler}
          />
        </div>
        <div className="new-expense__control">
          <label>Date</label>
          <input
            type="date"
            min="2022-01-01"
            max="2024-12-31"
            value={enteredDate}
            onChange={dateChangeHandler}
          />
        </div>
      </div>
      <div className="new-expense__actions">
        <button onClick={props.stopEditing}>Cancel</button>
        <button type="submit">Add Expense</button>
      </div>
    </form>
  );
};

export default ExpenseForm;

💩 헤맸던 점…

처음에 해당 과제를 할 때, 정답에서와 유사하게 flag를 이용하여 버튼만 보여주거나 폼만 보여주도록 하고 싶었다.

그런데 아무리 flag 를 변경을 해도 화면에서는 재렌더링을 해주지 않는 것이었다.

그래서 찾아보니 리액트에서 재렌더링이 되려면 몇가지 조건이 있었다.

  • state 변경이 있을 때
  • 새로운 props가 들어올 때
  • 부모 컴포넌트가 렌더링될 때
  • shouldComponentUpdate에서 true가 반환될 때
  • forceUpdate가 실행될 때

나의 경우 state가 아닌 그냥 일반 변수의 값만 변경해주었기 때문에 재렌더링이 되지 않았던 것이다.

참고: [react] 리렌더링이 되는 조건들 살펴보기

profile
🌸 좋은 코드를 작성하고 싶은 프론트엔드 개발자 ✨

0개의 댓글