221127.til

Universe·2022년 11월 27일
0

study

목록 보기
47/49

컴포지션(합성)

props.children 이라는 키워드를 사용해서 자주쓰는 jss 속성들을 묶을 수 있다.

	border-radius: 12px;
  box-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);

위 컴포넌트는 모두 일정한 외곽선 처리와 음영을 가지고 있는데,
공통적인 속성을 또 하나의 별도의 컴포넌트로 묶어주어 재사용에 편리하게 사용할 수 있다.
이 공통적인 속성들을 Card 라는 컴포넌트로 묶어보면
(일반적으로 웹 개발시 Card 라는 컴포넌트를 둥근 외곽선과 음영속성으로 자주 쓴다고 한다.)

import "./Card.css";
const Card = () => {
  return <div className="card"></div>;
};

export default Card;

이렇게 카드 컴포넌트를 정의하면
를 호출할 때 마다 .card 라는 클래스를 가진 스타일링 된 컴포넌트가 리턴된다.
Card 스타일을 적용할 컴포넌트에 가서 해당 속성을 적용시켜 보면,

<Card className="expense-item">
      <ExpenseDate date={props.date} />
      <div className="expense-item__description">
        <h2>{props.title}</h2>
        <div className="expense-item__price">$ {props.amount}</div>
      </div>
    </Card>

잘 되지 않는다.

재사용 가능한 wrapper 컴포넌트로 사용하려면 조금 더 특별한 키워드를 사용해야한다.
Card 컴포넌트에 className 이 제대로 설정되게 하려면

const Card = (props) => {
  const classes = "card " + props.className;
  return <div className={classes}>{props.children}</div>;
};

props.children 키워드는
A 컴포넌트 사이에 B 컴포넌트가 있을 때, A 컴포넌트에서 B 컴포넌트를 호출해주는 키워드이다.

작동방식은 이런 느낌이다.

props.children

메소드도 활용할 수 있는 모양
근데 확실히 React 는 오타에 너무 취약한 것 같다.
오타를 내는 내 손이 문제이긴 하지만
Components 는 내가 직접 명명한 이름의 커스텀 태그를 사용하는 느낌이므로,
그런 이름을 React 단에서 잡아주지 않는다.
조금 더 확실히 이런 오타를 잡아낼 수 있는 방법은 없을까 ?

JSX 가 동작하는 원리

JSX는 어떻게 동작하는걸까 ?
JSX는 HTML 과 비슷한 문법을 가진 언어를 새로 만든걸까? 하고 궁금했는데,
사실 단순하게는 React에서 정의한 React.createElement 메소드의 semantic sugar 일 뿐이라고 한다.
비교적 최근의 create React 도구에는

import React from 'react';

라는 import 가 되어있지 않는데,
과거에는 JSX를 이용하기위해 import React from ‘react’ 를 선언해주어야 했다.
JSX는 다른게 아니고

React.createElement(
	'div',
	{
		style:{
			width:'200px',
			color:'black',
			margin:'20px'
		}
	},
	'div 박스 입니다'
)

이런식으로 정의된 React 메소드를 조금 더 쉽게 쓰기위해 개발되었다.
신기하다.

useState

목표상태를 정의하고 리액트가 그 목표상태에 도달하게 하는 요소
그렇게 동적인 웹을 만들 수 있다.

사용자 이벤드에 반응하기

<button onClick={() => {console.log("click");}}> Change Title</button>

내장된 html 태그의 이벤트 핸들러를 추가한다.
리액트에서는 따로 이벤트리스너를 사용하지 않고 on~ 으로 시작하는 핸들러를 사용한다.
그런데 HTML 태그 안에 저런 함수의 코드들이 나열되어 있으면 미관상 좋지 않으니까

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

컴포넌트를 구성하는 함수의 상단에 함수를 선언하고,
이밴트 핸들러에 함수의 이름을 명시해주는게 좋다.

주의해야 할 점은,
함수의 실행, 그러니까 clickHandler() 식으로 명시하게 되면
JSX를 읽을 때 함수가 실행되어 버리기 때문에 함수의 이름만 명시해두어야 한다.
강사님이 함수에 이름을 짓는 관습을 소개해주셨는데 ~handler 라고 지으면 좋다고 한다.

useState()

const [a, setA] = useState(")

상태를 체크하는 변수를 만들 수 있다.
useState의 파라미터로 초기값을 설정할 수 있다.
컴포넌트별 인스턴스를 기반으로 해서 독립적인 state 를 갖게 한다.
꿀팁 : onChange = 모든 input 이벤트에 사용할 수 있다.
하나의 객체로 관리하려면 수동으로 복사해야 한다.
기존의 값들도 복사해야 한다.

가장 최근의 스냅샷을 가지게 하는 방법

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

동시에 많은 상태 업데이트를 계획한다면
오래되었거나 잘못된 상태 스냅샷에 의존할 수도 있다.
이 방법은 리액트는 함수에서 이 상태 스냅샷이 가장 최근 상태이고
항상 계획된 상태 업데이트를 염두에 두고 있다는 것을 보장한다.
이전 상태에 기반하는,
상태를 덮는게 아닌 이전 상태도 함께 저장해야하는 경우(객체로 되어있어서)
state 변경함수의 콜백함수의 리턴으로 구성해주면 좋다.

양방향 바인딩

input에 새로운 값(예를들어 input을 초기화한다던지)을 다시 전달할 수 있음

기본속성인 value 를 부여함

value={userInput.enteredTitle}

해당 form이 submit 될 때,

setUserInput({
      enteredTitle: "",
      enteredAmount: "",
      enteredDate: "",
    });

userInput 객체를 초기화 해주는 변경함수 로직을 설계하면 된다.
폼 전송에 따라 사용자의 입력을 모으거나 변경할 수 있게 해줄 수 있다.

상태 끌어올리기

자체 이벤트 핸들러를 생성해서 값으로 함수를 전달한다.
그 함수에 매개변수로 정보를 전달할 수 있다.

  1. 데이터를 전달받을 컴포넌트에서 커스텀 이벤트핸들러를 정의한다
<div className='new-expense'>
	<ExpenseForm onSaveExpenseData/>
</div>
  1. 이벤트에 들어갈 함수를 정의한다.
const saveExpenseDataHandler = (enteredExpenseData) => {
    const expenseData = {
      ...enteredExpenseData,
      id: Math.random().toString(),
    };
    props.

데이터를 spread operator 로 나열해서 새로운 오브젝트를 만들고
추가로 id도 추가해준다. 이 경우 무작위 난수를 추가해주었다.

  1. 커스텀 이밴트 핸들러에 해당 함수를 바인딩한다
<div className='new-expense'>
	<ExpenseForm onSaveExpenseData={saveExpenseDataHandler}/>
</div>
  1. 전달할 자식 컴포넌트에서 해당 데이터를 파라미터에 담아 부모 컴포넌트의 핸들러를 호출한다.
const submitHandler = (event) => {
    event.preventDefault();
    props.onSaveExpenseData(userInput);
    setUserInput({
      enteredTitle: "",
      enteredAmount: "",
      enteredDate: "",
    });
  };

이 경우에는 함수의 실행을 명시해주어야 한다.

이렇게 설계하면
submit 이벤트가 일어나면 부모 컴포넌트에서 props 로 담아온 이벤트 핸들러를 실행시킨다.
실행할 때, 파라미터 값으로 데이터를 전달한다.
자식 컴포넌트에서 실행한 이벤트 핸들러를 부모컴포넌트가 감지하면
해당 핸들러에 명시된 함수가 실행된다.
해당 함수는 파라미터 값으로 전달받은 데이터를 spread operator 로 나열해 id를 추가하여
새로운 객체를 생성해낸다.

lifting state up

같은 트리 단계에 있는 서로 다른 컴포넌트 끼리는 직접적으로 데이터를 주고받을 수 없다.
따라서 모두를 렌더링하는 상위 컴포넌트 까지 끌어올려서 props로 전달하는 방식을 사용한다.
쉽게 생각해서 트리에서 두 컴포넌트를 모두 자식으로 갖는 부모 컴포넌트까지
데이터를 끌어올려서 사용하면 된다는 것

렌더링 리스트 및 조건부 컨텐트

동적으로 데이터를 추가하려면
array.map 메소드를 이용하자.
두번째 파라미터로 keys도 쉽게 만들 수 있다.

다시 강조하자면 이전상태에 기반하는 최신상태의 스냅샷을 가지게 하려면

setExpenses((prevExpenses) => {
      return [expenseItem, ...prevExpenses];
    });

keys 가 필요한 이유

리액트는 동적으로 요소를 추가할 때 배열을 나열하는 과정을 거친다.
그런데 keys가 없다면 배열에 있는 컨텐츠와 다시 일치시키기 위해서
모든 목록을 체크하게 된다. 버그를 만들수도 있다.
잠재적인 성능이슈가 있을 수 있다.
리액트는 다른 방법이 없기 때문이다.
렌더링 된 아이템 수만 확인하는데,
리액트가 볼 때는 각각의 아이템이 비슷해보인다.
그래서 그걸 구분해줄 id가 필요하다.

key = {expense.id}

특정한 아이템에 대한 인덱스가 똑같을 수 도있고, 아이템에 직접 첨부된게 아니다.
고유한 id를 갖고 있어야 한다.
보통 db를 거친다면 고유id를 내려주니까 걱정 없다.
모든 아이템을 식별할 수 있으면 자료배열을 전부 돌지 않아도 되어 성능상의 이슈가 사라진다.
조금 더 효율적인 방법으로 업데이트가 가능하다는 뜻.
목록의 아이템을 맵핑할때는 항상 key가 있어야 한다.

react 에서 삼항연산자 대신 쓸 수 있는 조건부 연산자

{filteredExpenses.length === && <p>No expenses found</p>}

왼쪽에 있는 조건이 참이면 오른쪽에 있는 조건이 실행됨(렌더링 됨)
그래도 더 간결하게 HTML 을 구성하고 싶다면

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}
      />
    ));
  }

아예 함수처리를 하는 방법도 있다.

이번주 ,

정신없이 바쁘긴 한데 성과는 없는 그런 주.
부랴부랴 공부하는데 지식은 늘지않는 느낌이라 많이 초조하다.
정리하고 포스팅도 하고 깊게 알고 넘어가고 싶은데
시간은 한정되어있고 욕심만 많아서 자꾸 이러지도 저러지도 못하는게
음.. 너무 마음만 급한걸지도 모르겠다.

profile
Always, we are friend 🧡

0개의 댓글