'ReactJS'에 해당되는 글 2건

React 살펴보기 #8

Study/React 2017. 5. 15. 23:34


Conditional Rendering


React에서는 필요한 기능을 캡슐화한 고유한 컴포넌트를 만들 수 있다. 그리고 애플리케이션의 상태에 따라 이들 중 
일부만 렌더링할 수 있다.


React에서의 조건부 렌더링은 자바스크립트에서 조건문이 하는 방식과 동일하게 작동한다. 자바스크립트의 if나 
조건 연산자같은 연사자들을 이용하여 현재 상태를 표시할 elements를 생성할 수도 있고, React가 거기에 맞춰
UI를 업데이트하도록 할 수도 있다.


아래의 두 component를 살펴보자

function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}


사용자가 로그인 했는지의 여부에 따라 이들 중 하나의 component를 표시하는 Greeting이라는 component를
만들 수 있을 것이다.

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  // Try changing to isLoggedIn={true}:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

CodePen에서 살펴보기.


이 예제는 isLoggedIn이라는 prop의 값에 따라 다른 greeting을 렌더링 한다.


Element Variables


elements를 저장하기 위해 변수들을 사용할 수 있다. 이렇게 할 경우 출력의 나머지 부분이 변경되지 않고 있는 상태에서
일부 component를 조건부로 렌더링 하는데 도움이 된다.


로그인 버튼과 로그아웃 버튼을 표시하는 새로운 두 개의 component를 살펴보자

function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  );
}


이 component들 아래에 LoginControl이라는 상태를 갖는 component를 만들 것이다.


이 component는 자신의 현재 상태에 따라 <LoginButton /> 또는 <LogoutButton />를 렌더링 할 것이다.
또한 이전 예제에서 보았던 <Greeting />도 렌더링 할 것이다.

class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }

  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;

    let button = null;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('root')
);

CodePen에서 살펴보기.


변수를 선언하고 if문을 사용하는 것은 component를 조건부로 렌더링하는 좋은 방법이며, 때때로 더 짧은
문법을 사용하는 것이 더 좋을 것이다. JSX에는 몇가지 inline 조건 처리 방법이 있는데 아래에서 설명할 것이다.


Inline If with Logical && Operator


중괄호({})를 사용함으로써 JSX의 어떤 표현식들을 내장시킬 수 있다. 여기에는 자바스크립트의 논리 연산자인
&&이 포함된다. 이 연산자를 사용하는 것이 조건부로 element를 포함시키는데 더 편한 경우도 있다.

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('root')
);

CodePen에서 살펴보기.


이 예제는 자바스크립트 내에서 실행되기 때문에 true && expression은 항상 expression의 값을 갖게 되며
false && experssion은 항상 false의 값을 갖게 된다.


그러므로 만일 조건이 true라면 && 오른쪽의 element가 출력으로 표시된다. 반면에 조건이 false라면
React는 이 문장을 무시하고 건너 뛸 것이다.


Inline If-Else with Conditional Operator


elements를 inline으로 조건부 렌더링하는 방법에는 자바스크립트의 조건 연산자인 condition ? true : false 를 

사용하는 방법도 있다.


아래의 예제에서 작은 텍스트 블록을 렌더링 하기 위해 이 방법을 사용할 것이다.

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}
It can also be used for larger expressions although it is less obvious what's going on:

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (
        <LoginButton onClick={this.handleLoginClick} />
      )}
    </div>
  );
}


자바스크립트에서와 마찬가지로 어떤 것이 더 가독성이 좋은가 하는 선택은 당신에게 달려있다. 또한 조건이 더 복잡해
진다는 것은 component를 분리하기에 좋은 시점이란 것을 명심하자.


Preventing Component from Rendering


아주 드문 경우로 어떤 component가 다른 component로부터 렌더링 되었음에도 불구하고 숨겨야 할 경우가 있다.
이런 경우 렌더링될 출력값 대신 null을 리턴하면 된다.


아래의 예제에서 <WarningBanner />는 warn이라는 prop의 값에 따라 렌더링이 결정된다. 만일 prop의 값이
false라면 component는 렌더링되지 않는다.

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true}
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }

  handleToggleClick() {
    this.setState(prevState => ({
      showWarning: !prevState.showWarning
    }));
  }

  render() {
    return (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

CodePen에서 살펴보기.


component로부터 null을 리턴받게 되는 경우 render 메소드는 component의 lifecycle 메소드들을
작동시키지 않는다. 하지만 componentWillUpdate와 componentDidUpdate는 여전히 호출된다.







'Study > React' 카테고리의 다른 글

React 살펴보기 #8  (0) 2017.05.15
React 살펴보기 #7  (0) 2017.04.20
React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요

React 살펴보기 #6

Study/React 2017. 3. 27. 23:25


State and Lifecycle


이전 섹션 중 하나였던 시계 예제를 되돌아보자.
지금까지 우리는 UI를 업데이트 시키는 단지 한가지 방법만을 배웠다.
렌더링된 출력물을 바꾸기 위해 우리는 ReactDOM.render()를 호출했다.


function tick() {
 const element = (
	<div>
	  <h1>Hello, world!</h1>
	  <h2>It is {new Date().toLocaleTimeString()}.</h2>
	</div>
 );
 ReactDOM.render(
	element,
	document.getElementById('root')
 );
}

setInterval(tick, 1000);

CodePen에서 확인하기


이번 섹션에서는 Clock 컴포넌트를 진짜로 재활용 가능하고 캡슐화된 상태로 만드는 방법을 배울 것이다.
이 새로운 컴포넌트는 자체 타이머를 가질 것이고 매초마다 스스로 업데이트 할 것이다.


아래 코드를 캡슐화 시키는 것으로부터 시작하자.


function Clock(props) {
 return (
	<div>
	  <h1>Hello, world!</h1>
	  <h2>It is {props.date.toLocaleTimeString()}.</h2>
	</div>
 );
}

function tick() {
 ReactDOM.render(
	<Clock date={new Date()} />,
	document.getElementById('root')
 );
}

setInterval(tick, 1000);

CodePen에서 확인하기


하지만 위의 코드는 중요한 요건이 빠져있다. 타이머를 설정하고 매초마다 UI를 업데이트 하는 것은 Clock 내에
상세하게 구현되어야 한다는 것이다.


이상적인 형태로 우리는 시계 코드를 한번만 작성해 놓고 시계 스스로가 업데이트 할 수 있도록 할 것이다.


ReactDOM.render(
 <Clock />,
 document.getElementById('root')
);


이렇게 구현하기 위해서는 Clock 컴포넌트 안에 “state”를 추가해야 한다.
State는 props와 유사하지만 private 속성을 가지고 있고 전적으로 컴포넌트에 의해 관리된다.
이전 내용에서 class로 선언된 컴포넌트는 추가적인 기능들이 있다고 했는데 Local state가 바로 그 것이고
이 Local state는 class들 안에서만 사용할 수 있다.


Converting a Function to a Class


다음의 5단계를 통해 functional로 만들어진 Clock 컴포넌트를 class로 만들 수 있다.


  1. functional 컴포넌트와 동일한 이름으로 React.Component를 상속받는 ES6 클래스를 생성한다.
  2. render()라는 비어있는 메소드 하나를 추가한다.
  3. functional 컴포넌트의 함수 본체를 render() 메소드 안으로 옮긴다.
  4. render() 메소드 내에 있는 props를 this.props로 수정한다.
  5. 비어있는 functional 컴포넌트를 삭제한다.

class Clock extends React.Component {
 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}

CodePen에서 확인하기


이제 Clock 는 funtional이 아닌 class로 선언되었다.
Class로 선언되었기 때문에 local state나 lifecycle hooks같은 기능들을 사용할 수 있다.


Adding Local State to a Class


3단계를 거쳐 date객체를 props로부터 state로 옮길 것이다.


1) render() 메소드 안에 있는 this.props.date를 this.state.date로 바꾼다.


class Clock extends React.Component {
 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}


2) this.state에 초기값을 할당하는 클래스 생성자를 추가한다.


class Clock extends React.Component {
 constructor(props) {
	super(props);
	this.state = {date: new Date()};
 }

 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}


기본 생성자에게 props를 전달하는 방법에 주의하라.


constructor(props) {
	super(props);
	this.state = {date: new Date()};
 }


Class 컴포넌트에서는 기본 생성자를 호출할 때 반드시 props를 파라미터로 넘겨야 한다.


3) <Clock /> element에서 date prop을 삭제한다.


ReactDOM.render(
 <Clock />,
 document.getElementById('root')
);


마침내 timer 코드를 시계 컴포넌트 안에 추가하였다.
결과 코드는 다음과 같다.


class Clock extends React.Component {
 constructor(props) {
	super(props);
	this.state = {date: new Date()};
 }

 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}

ReactDOM.render(
 <Clock />,
 document.getElementById('root')
);

CodePen에서 확인하기


다음으로는 Clock 컴포넌트가 자신의 timer를 설정하고 매초마다 스스로 업데이트되도록 만들 것이다.


Adding Lifecycle Methods to a Class


많은 컴포넌트를 가진 애플리케이션에서 컴포넌트가 사용한 리소스를 컴포넌트가 사용 종료될 때 메모리에서
해제 시키는 것은 매우 중요하다.


우리는 Clock컴포넌트가 처음 DOM 내에 렌더링 되는 시점에만 timer를 설정하려고 한다. React에서는
이 과정을 “mounting”이라고 한다.


한편 DOM에서 생성된 컴포넌트가 제거될 때마다 timer를 지우려고 한다. React에서는 이 것을 “unmounting”
이라고 한다.


컴포넌트 내에는 컴포넌트가 mount 되거나 unmount 될 때마다 어떤 코드를 실행하는 특별한 메소드를 선언할
수 있다.


class Clock extends React.Component {
 constructor(props) {
	super(props);
	this.state = {date: new Date()};
 }

 componentDidMount() {

 }

 componentWillUnmount() {

 }

 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}


이런 메소드들을 “lifecycle hooks”라고 부른다.
componentDidMount() hook는 컴포넌트의 출력이 DOM에 렌더링 된 후 실행된다. 그래서 이 메소드는
timer를 설정하기에 좋은 위치이다.


componentDidMount() {
	this.timerID = setInterval(
	  () => this.tick(),
	  1000
	);
 }

this에 timer ID를 어떻게 저장하는지에 유의하라.


this.props는 React 자체에 의해 설정되고 this.state는 특별한 의미가 있지만 만일 시각적인 출력에 
사용되지 않는 어떤 정보를 저장할 필요가 있는 경우에는 수작업을 통해 자유롭게 필드를 추가할 수 있다.


render()안에서 사용되지 않는 것들은 state가 되어서는 안된다.


componentWillUnmount() lifecycle hook에서는 timer를 지울 것이다.


componentWillUnmount() {
	clearInterval(this.timerID);
 }


마침내 매초마다 실행되는 tick() 메소드를 구현할 차례이다.
컴포넌트의 local state를 주기적으로 업데이트하기 위해 this.setState()를 사용할 것이다.


class Clock extends React.Component {
 constructor(props) {
	super(props);
	this.state = {date: new Date()};
}

 componentDidMount() {
	this.timerID = setInterval(
	  () => this.tick(),
	  1000
	);
 }

 componentWillUnmount() {
	clearInterval(this.timerID);
 }

 tick() {
	this.setState({
	  date: new Date()
	});
 }

 render() {
	return (
	  <div>
		<h1>Hello, world!</h1>
		<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
	  </div>
	);
 }
}

ReactDOM.render(
 <Clock />,
 document.getElementById('root')
);

CodePen에서 확인하기


이제 Clock은 매초 작동할 것이다.


무슨일이 일어나고 있는지, 그리고 메소드가 호출되는 순간 어떤 순서로 진행되는지 간단하게 살펴보자


  1. React.render()로 <Clock />이 전달되면 React는 Clock 컴포넌트의 생성자를 호출한다. Clock은 현재 시간을 표시해야 하기 때문에 현재 시간을 가진 객체로 this.state를 초기화 한다. 뒤에 이 state를 업데이트한다.
  2. 그리고 나서 React는 Clock 컴포넌트의 render()메소드를 호출한다. 이러한 방식으로 React는 화면에 무엇을 표시해야 하는지 알게 된다. 이후 React는 Clock의 렌더링 출력에 맞게 DOM을 업데이트한다.
  3. Clock의 출력이 DOM에 삽입되면, React는 componentDidMount() lifecycle hook를 호출한다. 그 안에서 Clock 컴포넌트는 브라우저에게 매초마다 tick()을 호출하는 timer를 설정하도록 요청한다.
  4. 매초마다 브라우저는 tick() 메소드를 호출한다. 그 안에서 Clock 컴포넌트는 현재 시간을 가지고 있는 객체를 가지고 setState() 메소드를 호출함으로써 UI 업데이트를 계획한다. setState() 호출 덕분에 React는 state가 변경된 것을 알 수 있게 되고 render() 메소드를 다시 호출하여 화면에 어떤 것이 표시되어야 하는지 알게 된다. 이 때는 render() 내에 있는 this.state.date가 달라지고 렌더링 출력에 업데이트된 시간이 포함된다. 이를 통해 React는 DOM을 업데이트 한다.
  5. Clock 컴포넌트가 DOM에서 제거되면 React는 componentWillUnmount() lifecycle hook를 호출하게 되고 timer가 중지된다.

Using State Correctly


setState()에 대해 반드시 알아야 할 사항이 3가지 있다.


Do Not Modify State Directly


아래 예제는 컴포넌트를 다시 렌더링하지 않는다.


// Wrong
this.state.comment = 'Hello';


대신에 setState()를 사용해야 한다.


// Correct
this.setState({comment: 'Hello'});


this.state를 이용하여 할당할 수 있는 유일한 위치는 생성자이다.


State Updates May Be Asynchronous


React는 성능 향상을 위해 다수의 setState() 호출을 하나의 업데이트로 일괄 처리한다.
this.props와 this.state는 비동기방식으로 처리되기 때문에 이후에 올 state에 대한 계산 결과 값을 
신뢰해서는 안된다.


예를들어 다음 코드는 카운터 업데이트에 실패할 것이다.


// Wrong
this.setState({
 counter: this.state.counter + this.props.increment,
});


이 코드를 수정하기 위해서는 파라미터로 객체가 아닌 함수를 받는 두 번째 형식의 setState()를 사용해야 한다.
파라미터로 전달되는 함수는 첫 번째 인자로 이전 state를, 두 번째 인자로는 업데이트된 시점의 props를 받는다.


// Correct
this.setState((prevState, props) => ({
 counter: prevState.counter + props.increment
}));


위 코드에서는 화살표 함수를 사용했지만 일반 형식의 함수를 사용해도 된다.


// Correct
this.setState(function(prevState, props) {
 return {
	counter: prevState.counter + props.increment
 };
});


State Updates are Merged


setState()를 호출할 때 React는 현재 state에 제공한 객체들을 병합한다.
아래 예제를 보면 state에는 몇개의 독립된 변수들이 있다.


constructor(props) {
	super(props);
	this.state = {
	  posts: [],
	  comments: []
	};
 }


그리고 서로 다른 setState()를 호출함으로써 각각의 변수를 독립적으로 업데이트 할 수 있다.


componentDidMount() {
	fetchPosts().then(response => {
	  this.setState({
		posts: response.posts
	  });
	});

	fetchComments().then(response => {
	  this.setState({
		comments: response.comments
	  });
	});
 }


병합은 얕게 이루어지기 때문에 this.setState({comments})는 this.state.posts를 온전하게 두지만 
this.state.comments는 완전히 대체한다.


The Data Flows Down


부모 컴포넌트나 자식 컴포넌트 모두 어떤 컴포넌트가 state를 가지고 있는 컴포넌트인지 state가 없는 컴포넌트인지
알지 못한다. 또한 함수로 정의된 컴포넌트인지 클래스로 만들어진 컴포넌트인지도 알 수 없다.


이러한 이유로 state는 종종 local 또는 encapsulated라고 불리운다. state를 소유하고 설정하는 컴포넌트 외에는
state에 접근할 수 없다.


어떤 경우에는 자식 컴포넌트에 props를 통해 state를 전달하기도 한다.


<h2>It is {this.state.date.toLocaleTimeString()}.</h2>


이러한 방식은 사용자 정의 컴포넌트에서도 작동한다.


<FormattedDate date={this.state.date} />


FormattedDate 컴포넌트는 date를 props로 전달받게 되는데, 이것이 Clock의 state로부터 온 것인지,
Clock의 props로부터 온 것인지, 아니면 직접 손으로 타이핑을 해서 전달 받은 것인지 알지 못한다. 


function FormattedDate(props) {
 return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

CodePen에서 확인하기


이 것을 일반적으로 “top-down” 또는 “unidirectional” data flow라고 부른다. 어떤 state든 반드시 특정 
컴포넌트에 소유되어있어야 하고 그 state로부터 파생된 어떤 data 또는 UI라도 트리 구조의 아래쪽으로만 영향을
미칠 수 있다.


컴포넌트의 트리 구조를 props의 폭포로 생각한다면 각 컴포넌트의 state는 임의의 지점에서 합류하는 또다른
수원으로 생각할 수 있으며 이 또한 아래로 흐르게 되는 것이다.


모든 컴포넌트가 실제로 독립되어있다는 것을 보여주기 위해 3개의 <Clock>을 가진 App을 생성하였다.


function App() {
 return (
	<div>
	  <Clock />
	  <Clock />
	  <Clock />
	</div>
 );
}

ReactDOM.render(
 <App />,
 document.getElementById('root')
);

CodePen에서 확인하기


각각의 Clock은 자신이 가진 timer만을 설정하고 있으며 각각의 timer는 독립적으로 업데이트된다.


React App들에서는 stateful인지 stateless인지 하는 것은 시간이 지남에 따라 변경될 수 있는 세부 구현 사항으로
여겨진다. stateless 컴포넌트를 stateful 컴포넌트 내에서 사용할 수도 있고 그 반대도 가능하다.






'Study > React' 카테고리의 다른 글

React 살펴보기 #8  (0) 2017.05.15
React 살펴보기 #7  (0) 2017.04.20
React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요