바닐라로 상태 기반 렌더링 컴포넌트 만들기 5 - 코어 컴포넌트 사용해보기

Sonny·2022년 4월 25일
6

Vanilla

목록 보기
5/5
post-thumbnail

이 시리즈의 이전 글은 아래로...

레포지토리 보러가기🗃

Github - Vanilla Component

feat/#3_ExampleComponent 브랜치에서 자세히 확인 하실 수 있습니다! 😊

코어 컴포넌트 활용 화면 🎬

코어 컴포넌트 사용시, 주의 사항 🚨

컴포넌트는 항상 하나의 부모 프래그먼트로 감싸주어야 합니다!

올바르게 작성한 경우 ✅

template() {
	return `
    	<div>
        	<h1>Hello, World!</h1>
            <p>Hello, Component!</p>
        </div>
    `
}

올바르지 못하게 작성한 경우 ⛔️

template() {
	return `
        <h1>Hello, World!</h1>
        <p>Hello, Component!</p>
    `
}

코어 컴포넌트 사용법 - 생성 📚

1. 이벤트가 있는 경우

// components/example/Button

interface IButtonState {
  onClick(): void
}

...
export default class Button extends Component<IButtonState> {
  template(): string {
    const children = this.node.textContent || '버튼'

    return `
      <button>${children}</button>
    `
  }

  setEvent(): void {
    this.node.addEventListener('click', this.state.onClick)
  }

  clearEvent(): void {
    this.node.removeEventListener('click', this.state.onClick)
  }
}
  • render시, 실행될 html을 반환 해줍니다.
  • render 이후, 현재 node에 이벤트를 바인딩해줍니다.
  • unMount(update)시, 현재 node의 이벤트를 제거해줍니다.

2. 이벤트가 없는 경우

// components/example/ExampleText
interface IExampleTextState {
  text: string
}

...
export default class ExampleText extends Component<IExampleTextState> {
  template(): string {
    const { text } = this.state

    return `
      <div>
        <h1>Example Component</h1>
        <p>${text}</p>
      </div>
    `
  }
}
  • Present용 컴포넌트라면 현재 상태의 값을 html에 문자열의 형태로 반영 해주면 끝!

컴포넌트 사용법 - 연결 (부착) 📚

잘 만들어진 하위 컴포넌트를 상위 컴포넌트에서 사용해보자!

초기 상태 정의

// App.ts
...
constructor({ node }: IComponentParams<IAppState>) {
  const initalState = {
    initalText: '이 Text가 변경될 때는 렌더링이 되지 않아요!',
    text: `initalState`,
    handleChangeText: (): void => {
      this.setState({
        text: `Changed State: ${Math.random()}`
      })
    },
    handleChangeInitalText: (): void => {
      this.setState({
        initalText: `다른 컴포넌트의 렌더링 영향으로 렌더링이 되었습니다! ${Math.random()}`
      })
    },
    onClick: (type: string): void => {
      if (type === 'initalText') {
        this.state.handleChangeInitalText()
        return
      }

      this.state.handleChangeText()
    }
  }

  super({ node, initalState })
}
  • 부모 컴포넌트로부터 넘겨받은 initalState가 없는 경우, constructor()에서 새롭게 정의해준 뒤, super() 메서드를 호출합니다.

하위 컴포넌트가 들어갈 자리를 태그로 만들어 표시

// App.ts

template(): string {
  return `
    <main id="App">
      <ExampleText></ExampleText>
      <HandleTextButton>Change Text!</HandleTextButton>
      <HandleInitalTextButton>Change InitalText</HandleInitalTextButton>
    </main>
  `
}
  • template()에 하위 컴포넌트가 들어갈 자리를 태그로 만들어 표시합니다.
    ( document.createElement()로 만들어도 되지만 편의성 및 컴포넌트 확인을 위해 위와 같이 작성하였습니다! )

하위 컴포넌트를 호출

// App.ts

...
attachChildComponent(): void {
  const { initalText, text, onClick } = this.state

  const exampleText = new ExampleText({
    node: selectEl(this.node, 'ExampleText'),
    preventRenderStateKey: ['initalText'],
    initalState: {
      initalText,
      text
    }
  })

  new Button({
    node: selectEl(this.node, 'HandleTextButton'),
    initalState: {
      onClick: (): void => {
        onClick('text')
      }
    }
  })

  new Button({
    node: selectEl(this.node, 'HandleInitalTextButton'),
    initalState: {
      onClick: (): void => {
        onClick('initalText')
      }
    }
  })

  this.subscribe(exampleText)
}
  • attachChildComponent()에 부착할 컴포넌트를 new 키워드와 함께 호출합니다.
  • 부모 컴포넌트의 상태가 변경될 때마다 알림을 받고싶은 경우, subscribe() 메서드에 해당 컴포넌트를 넣어 추적합니다.
  • 렌더링이 필요없는 경우, preventRenderKeys에 배열로 해당 키를 넣어줍니다.
    • 다른 컴포넌트에 의해 렌더링이 되는 경우, 상태는 계속 추적하고 있었기 때문에 변경된 상태의 값으로 렌더링이 됩니다.
    • 만약 preventRenderKeys에 포함되어 있지 않은 키값이 setState()되는 경우, 여부와 관계없이 무조건 렌더링 됩니다.

Q. 구독을 안하게되면 어떻게 되나요?

Button 컴포넌트와 같이 App 컴포넌트의 상태가 변경이 되었을 때, 리렌더링이 필요가 없는 컴포넌트는 구독을 해주지 않아도 됩니다.
=> 구독을 안해주면 초기 상태만 넘겨받은 뒤, 부모 컴포넌트의 상태를 추적하지 않습니다.

휴,,, 아직 any로 되어 있는 타입들도 많고 state에 대한 방어처리, 렌더링 최소화 처리에 대한 아이디어는 많지만.. 시간이 없으니 차근차근 해보는걸로....

profile
FrontEnd Developer

1개의 댓글

comment-user-thumbnail
2022년 4월 26일

다음편 기대돼요 !!!!!

답글 달기