렌더링 리스트 및 조건부 Content

ZeroJun·2022년 6월 8일
0

React

목록 보기
4/13

데이터 동적 렌더링

jsx는 배열형태도 랜더링 해주기 때문에 아래와 같이 map을 활용하여 동적으로 리스트를 렌더링 할 수 있다.
굳이 변수에 담지 않고, 중괄호 안에 직접 map을 하는 것도 가능하다.

  // map활용
  const ExpenseItemList = props.items.map((expense, key) => (
    <ExpenseItem
      key={key}
      title={expense.title}
      amount={expense.amount}
      date={expense.date}
    />
  ));

  return (
    <div>
      <Card className="expenses">
        <ExpensesFilter
          selected={filteredYear}
          onChangeFilter={filterChangeHandler}
        />
        {ExpenseItemList}
      </Card>
    </div>
  );
}

State저장 목록 사용

렌더링할 데이터가 추가되었을 경우 그것을 반영하기 위해 State를 사용한다.

// App.js
const DUMMY_EXPENSES = [
  {
    id: "e1",
    title: "Toilet Paper",
    amount: 94.12,
    date: new Date(2020, 7, 14),
  },
  { id: "e2", title: "New TV", amount: 799.49, date: new Date(2021, 2, 12) },
  {
    id: "e3",
    title: "Car Insurance",
    amount: 294.67,
    date: new Date(2021, 2, 28),
  },
  {
    id: "e4",
    title: "New Desk (Wooden)",
    amount: 450,
    date: new Date(2021, 5, 12),
  },
];

const App = () => {
  const [expenses, setExpenses] = useState(DUMMY_EXPENSES);

  //const addExpenseHandler = (expense) => {
  //  setExpenses([expense, ...expenses])
  //};
  
  // 더 나은 방법
  const addExpenseHandler = (expense) => {
    setExpenses((prevExpenses) => {
      // 이 함수는 자동으로 최신의 스냅샷을 인자로 받는다.
      return [expense, ...prevExpenses];
    });
  };
  // 이것은 동일한 상태의 이전 스냅샷을 기반으로 하는 경우에
  // 상태를 업데이트할 수 있는 깔끔한 방법이다.
  
  return (
    <div>
      <NewExpense onAddExpense={addExpenseHandler} />
      <Expenses items={expenses} />
    </div>
  );
};

함수형 업데이트

setState는 비동기적으로 동작하고, 리액트가 효율적으로 렌더링하기 위해 여러 개의 상태 값 변경 요청을 batch(일괄 처리) 처리하기 때문에 아래 코드의 value 결과는 1이 된다.

setState는 변경된 사항을 기억하지 않고 마지막 업데이트만 적용하여 다음 렌더링에 쓰인다.

export default function App() {
  const [value, setValue] =  useState(0)

  const onClick = () => {
    setValue(value+1)
    setValue(value+1)
    setValue(value+1)
  }
  
  return (
    <div className="App">
      <button onClick={onClick}>+</button>
      <h1>{value}</h1>
    </div>
  );
}

// A = 0

setValue(value+1)
// ① B = A + 1 => 2

setValue(value+1)
// ② B = A + 1 => 2

setValue(value+1)
// ③ B = A + 1 => 2

이 문제를 해결하고, 동기적으로 실행하기 위해서 함수형 업데이트가 사용된다.
함수현 업데이트는 이렇게 업데이트된 값을 저장하기 때문에 여러번의 업데이트가 적용되어 다음 렌더링에 사용된다.

export default function App() {
  const [value, setValue] =  useState(0)

  const onClick = () => {
    setValue(prev => prev+1)
    setValue(prev => prev+1)
    setValue(prev => prev+1)
  }
  
  return (
    <div className="App">
      <button onClick={onClick}>+</button>
      <h1>{value}</h1>
    </div>
  );
}

Key 이해하기

key가 없는 상태에서 리액트는 배열을 렌더링할 때 마지막에 요소를 추가한 후 전체를 다시 배열과 매칭시키면서 렌더링한다. 이는 성능적으로 매우 비효율적이다. 또한 상태 관리 측면에서도 버그가 발생할 확률이 높아진다.

key는 새로운 요소가 어디에 추가되어야 하는지 리엑트에게 알려주는 것이다. key는 어떤 컴포넌트든 key라는 이름으로 프로퍼티로 추가할 수 있다.

// 배열에 key로 사용할만한 요소가 있을 경우.
  const ExpenseItemList = props.items.map((expense) => (
    <ExpenseItem
      key={expense.id} // key
      title={expense.title}
      amount={expense.amount}
      date={expense.date}
    />
  ));

// key로 사용할만한 요소가 없을 경우 (버그를 발생시킬 수 있어 좋지 않다.)
  const ExpenseItemList = props.items.map((expense, idx) => (
    <ExpenseItem
      key={idx} // key
      title={expense.title}
      amount={expense.amount}
      date={expense.date}
    />
  ));

리스트 filtering하기

기존 배열에서 filter처리한 배열을 그대로 map으로 처리해주면 된다.

  const filteredExpenses = props.items.filter((expense) => {
    return expense.date.getFullYear().toString() === filteredYear;
  });

  const ExpenseItemList = filteredExpenses.map((expense) => (
    <ExpenseItem
      key={expense.id}
      title={expense.title}
      amount={expense.amount}
      date={expense.date}
    />
  ));

조건부 내용 출력하기

삼항 연산자를 사용 하거나 단축평가를 사용하여 랜더링할 수 있다.

        {filteredExpenses.length === 0 ? (
          <p>No Expenses found.</p>
        ) : (
          ExpenseItemList
        )}

        {filteredExpenses.length === 0 && <p>No Expenses found.</p>}
        {filteredExpenses.length > 0 && ExpenseItemList}

혹은 return하기 전에 로직을 완전히 처리한다면 return문 속의 jsx를 깔끔하게 유지할 수 있다. (이 방식이 가장 권장된다.)

  let expensesContent = <p>No Expenses found.</p>;
  if (filteredExpenses.length > 0) {
    expensesContent = filteredExpenses.map((expense) => (
      <ExpenseItem
        key={expense.id}
        title={expense.title}
        amount={expense.amount}
        date={expense.date}
      />
    ));
  }
  return (
    <div>
      <Card className="expenses">
        <ExpensesFilter
          selected={filteredYear}
          onChangeFilter={filterChangeHandler}
        />
        {expensesContent}
      </Card>
    </div>
  );
}

차트 추가하기

0개의 댓글