티스토리 뷰

컴포넌트

(Arrow Function으로) 컴포넌트를 만들어본다. 

import React from "react";

const App = (props) => {
  const { sessionList } = props.store; //비구조화 할당을 해줌
  return (
    <div>
      <header>
        <h1>Reacct and TypeScript</h1>
      </header>
      <ul>
        
        {sessionList.map((session) => (
          <li>{session.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default App;

 

컴포넌트 분리

지난번에 언급했듯, 코드 작성 시 몇가지 원칙들이 있다.

'네이밍은 잘 지어야 한다.'와 같은.

 

상위에서 성격이 다른 것을 분리한다. 이 때, 언제 쪼갤지, 어떻게 잘게 쪼갤지 고민해야한다.

추후 코드가 비대해지기 전에 아래처럼 컴포넌트를 쪼개도록 해보자.

import React from "react";

const SessionItem = ({title}) => (
  <li>{title}</li>
)

const App = (props) => {
  const { sessionList } = props.store; //비구조화 할당을 해줌
  return (
    <div>
      <header>
        <h1>Reacct and TypeScript</h1>
      </header>
      <ul>
      {sessionList.map((session) => (
          <SessionItem title={session.title} />
        ))}
      </ul>
    </div>
  );
};

export default App;

 

 

 

상태 만들기

그런데, 위의 컴포넌트는 상태가 없다. '정렬' 상태를 만들어보자.

const App = (props) => {
  const { sessionList } = props.store;
  const orderedSessionList = sessionList.map((session, i) => ({
    ...session,
    order: i
  }))

  return (
    ...
  );
};

orderedSessionList를 추가해준다. 기존 sessionList에 order 항목을 추가해주는 코드이다.

 

 

 

클래스 컴포넌트에서의 상태

 

class 컴포넌트는 new로 생성하고 render()를 실행해 주므로 값이 바뀌었을때 render를 실행할 수 있다.

리액트가 변화를 감지할 수 있도록 setState()를 실행하면 된다.

class ClassApp extends React.Component {
  constructor(props) {
    super(props);

    this.onToggleDisplayOrder = this.onToggleDisplayOrder.bind(this);
    this.state = {
      displayOrder: 'ASC' //기본값을 넣어줌
    }
  }

  onToggleDisplayOrder() {
    this.setState({
      displayOrder: displayOrder === 'ASC' ? 'DESC' : 'ASC'
    }) 
  }

이렇게 함수를 선언문으로 작성할 경우 this는 실행 컨텍스트를 따르므로 컨텍스트를 위해 this를 bind 해주어야한다.

반면,  arrow function은 렉시컬 컨텍스트이므로 컴파일 타임의 흐름대로 문맥상 this를 알 수 있다.

 

클래스 컴포넌트에서는 상태가 여러 메소드에 분산되어있다.

class ClassApp extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      displayOrder: 'ASC' 
    }
  }

  toggleDisplayOrder = () => {
    this.setState({
      displayOrder : displayOrder === 'ASC'? 'DESC': 'ASC'
    })
  } 

  render() {
    return(
      <div>...
        <button onClick={this.toggleDisplayOrder}></button>
      </div>
    )
  }
}

 

 

함수형 컴포넌트에서의 상태

 

함수형 컴포넌트는 원래 상태를 가질수 없다. 하지만 hook을 쓰면 가능해진다.

const App = (props) => {
  const [displayOrder, toggleDisplayOrder] = React.useState('ASC')
  const { sessionList } = props.store;
  const orderedSessionList = sessionList.map((session, i) => ({
    ...session,
    order: i
  }))

  const onToggleDisplayOrder = () => {
    toggleDisplayOrder(displayOrder === 'ASC' ? 'DESC': 'ASC')
  } 

  return (
    <div>
      <header>
        <h1>Reacct and TypeScript</h1>
      </header>
      <p>
        전체 세션 갯수 : 4개
      </p>
      <button onClick={onToggleDisplayOrder}>재정렬</button>
      <ul>
      {orderedSessionList.map((session) => (
          <SessionItem title={session.title} />
        ))}
      </ul>
    </div>
  );
};

클래스 컴포넌트와 달리 상태가 응집성을 가지고 있다.

 

* 함수형 컴포넌트에서 dispatcher는 콜백으로 받아야한다. 

* 참고로, hook자체는 closure가 아니다.

 

 

useEffect(() => {
    구독()
	
    return () => {
    	구독해제()
    }
})

hook의 하나인 useEffect는 위와 같이 사용할 수 있다.

이 함수는 리액트 바깥에서의 사이드 이펙트를 몰아놓은 코드 즉, 사이드 이펙트가 생기는 일들을 처리하는 함수이다.

리턴을 함수로 해주면 UI에서 사라질떄 호출을 해준다.

 

 


 

비동기와 Generator

Generator

function* foo() {

}

Async

async function bar() {

}

 

둘 다 Promise와 밀접한 관계가 있다.

 

const x = 10;
const y = x * 10;

위의 코드는 동시에 실행이 불가하다

const x = () => 10;
const y = x() * 10;

이렇게 하면 x가 지연호출이 된다.(x값이 호출되는 시점에 결정됨)

Promise는 이 지연과 유사하다.

 

 

Promise

const p = new Promise(function(resolve, reject){
    //Promise는 두개의 함수를 전달하기 위해 함수를 받음
    resolve()
}

p.then(function(r) {

}

자바스크립트 클로저 때문에 Promise 안에 있는 다른 함수 안에 넣어 코드를 지연 시킬 수 있다.

const p = new Promise(function(resolve, reject){
	
    setTimeout(() => {
     	resolve()
    }, 1000)
   
}

 

Generator

funtion* make() {
	return 1;
}

const i = make();

//일반적인 함수의 경우 i = 1
//제너레이터는 객체를 리턴함

제너레이터는 실제로 코루틴 함수의 구현체이다.

함수는 입력값을 받고 계산을 한뒤, 리턴을 한다. * 리턴이 없는건 프로시져

그런데 리턴을 여러번 할 수 있는 것이다.

다시 함수가 호출될때 마지막 리턴 지점부터 다시 시작한다.

(어떤 값을 계속 생산해내는 거라 명칭이 제너레이터인듯)

 

function* makeNumber() {
	let num = 1;
    
    while(true){
    	yield num++ //yield는 제너레이터 안에 리턴의 의미로 사용됨
    }	
}

const i = makeNumber();

위의 코드는 호출시 리턴되는 객체이고, 실행될 준비만 한다.

i.next()를 했을때 실제로 제너레이터 함수가 실행이 된다. 

그리고 값이 아닌 객체를 반환한다.

→ 제너레이터 함수 바깥에선 끝났는지 여부를 체크할 수 없으므로 done이라는 플래그를 넘겨주기 위해서이다.

 

yield는 return 처럼 함수를 끝내는 게 아니라 값만 반환하는 역할이다.

.next() 메소드를 호출하면 yield가 있는 곳까지 코드가 실행된다.

이 것은 마치 바깥쪽 영역과 커뮤니케이션하는 느낌이다.

next()에 파라미터로 값을 넘기면 yield가 받을 수도 있다.

 

const delay = ms => new Promise((resolve) => setTimeout(resolve, ms));

function* main() {
	yield delay(3000);
	console.log('3sec');
} // 동기코드처럼 보임

const it = main(); //아직 호출안됨
const { value } = it.next() //promise 객체가 옴

value.then(() => {
	it.next();
   })

delay(3000).then(() => {
	console.log('3sec');
})

generator는 함수 바깥쪽에서 안쪽을 제어하고, 안쪽에서는 비동기도 동기처럼 풀 수 있다.

이런방식을 쓰는게 바로 Redux-saga이다.

yield는 iterable한 객체를 반환하며, 프라미스가 아닌 상황에서도 사용할 수 있다.

 

Async

async function main2() {
    await delay(3000);
    console.log('3sec');
}

main2();

Promise에 최적화 되어있다. Generator와 달리 다음에 Promise가 와야한다.

이 내부도 Generator로 구성되어 있다. 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함