카테고리 없음

[공식문서 읽기]Keeping Components Pure

아보카도 있었어! 2023. 7. 23. 22:26

1. Fix a broken clock

| 자정부터 오전 6시까지는 'night' className을 줘서 시계 색상 반전 시키기

 

내가 쓴 답

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    return (
    <h1 id="time" className='night'>
      {time.toLocaleTimeString()}
    </h1>      
    )

  }
  return 
    <h1 id="time" className='day'>
      {time.toLocaleTimeString()}
    </h1>

}

결과

 

React Solution

| 클래스명을 계산하여 렌더링 출력에 포함한다

export default function Clock({ time }) {
  let hours = time.getHours();
  let className;	// calculation할 변수 선언
  
  if (hours >= 0 && hours <= 6) {		// 렌더링 도중에 계산
    className = 'night';
  } else {
    className = 'day';
  }
  return (		// 계산된 변수 값 출력
    <h1 className={className}>
      {time.toLocaleTimeString()}
    </h1>
  );
}

이 예제에서 side effect, DOM 수정이 전혀 필요하지 않습니다. JSX를 반환하기만 하면 됩니다.

 

2. Fix a broken profile

| 컴포넌트마다 다른 사람 프로필 출력하기

 

내가 쓴 답

import Panel from './Panel.js';
import { getImageUrl } from './utils.js';

export default function Profile({ person }) {

  return (
    <Panel>
      <Header currentPerson={person} />
      <Avatar currentPerson={person} />
    </Panel>
  )
}

function Header({currentPerson}) {
  return <h1>{currentPerson.name}</h1>;
}

function Avatar({currentPerson}) {
  return (
    <img
      className="avatar"
      src={getImageUrl(currentPerson)}
      alt={currentPerson.name}
      width={50}
      height={50}
    />
  );
}

펼쳤을 때
접었을 때(collapse)

 

React solution

React의 컴포넌트는 변수가 아니라 props를 통해 이루어진다

import Panel from './Panel.js';
import { getImageUrl } from './utils.js';

export default function Profile({ person }) {
  return (
    <Panel>
      <Header person={person} />		// props로 현재 컴포넌트에 해당하는 사람 데이터를 전달
      <Avatar person={person} />
    </Panel>
  )
}

function Header({ person }) {	
  return <h1>{person.name}</h1>;
}

function Avatar({ person }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={50}
      height={50}
    />
  );
}

React는 컴포넌트 함수가 특정 순서로 실행된다는 것을 보장하지 않으므로 변수를 설정하여 컴포넌트 간 통신이 불가능합니다. 모든 통신은 props를 통해 이루어져야 합니다.

 

3. Fix a broken story tray

내가 쓴 답

export default function StoryTray({ stories }) {

  return (
    <ul>
      {stories.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
      <li key='create'>
        {'Create Story'}
      </li>
    </ul>
  );
}

결과

 

React solution

 

export default function StoryTray({ stories }) {
  return (
    <ul>
      {stories.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
      <li>Create Story</li>
    </ul>
  );
}

시계가 업데이트될 때마다 'Create Story'가 두 번 추가된다. (왜 두 번? Strict Mode에서 컴포넌트를 두 번 호출했기 때문에)

StoryTray 컴포넌트는 순수하지 않다. prop으로 받아온 stories 배열의 push 메서드를 호출하면 storyTray가 렌더링되기 전에 생성된 객체를 수정하는 일이기 때문에, 이로 인해 버그가 발생하고 예측하기 매우 어렵게 된다.

 

가장 간단한 수정방법은 배열을 전혀 건드리지 않고 'Create Story'를 별도로 렌더링하는 것이다.

 

또는 push하기 전에 기존에 받은 배열을 복사해서 새로운 배열을 만든다.

export default function StoryTray({ stories }) {
  // Copy the array!
  let storiesToDisplay = stories.slice();

  // Does not affect the original array:
  storiesToDisplay.push({
    id: 'create',
    label: 'Create Story'
  });

  return (
    <ul>
      {storiesToDisplay.map(story => (
        <li key={story.id}>
          {story.label}
        </li>
      ))}
    </ul>
  );
}

이렇게 하면 변경이 local로 유지되고 렌더링 함수가 순수해집니다. 배열의 메서드 중 원래 배열을 변경시키는 메서드와 새로운 배열을 반환하는 메서드를 알아두면 유용합니다.

 

그래서 리액트가 결국 추구하는 purity는 무엇일까?

 

1. 우선 prop을 변경하지 않는다. (변경 하려면 기존 값을 복사한 새로운 변수를 만들어서 변경을 local하게 만들기)

2. 렌더링은 계산이다. 무언가를 '해내려고'해서는 안된다.

3. 바인딩되는 데이터의 변경이 필요하다면 렌더링 사이에 연산하면 된다. 렌더링은 계산이다.

4. React는 컴포넌트 함수가 특정 순서로 실행할 것을 보장하지 않는다.