본문 바로가기

Front-End/React

[React] Describing the UI

한달 전에 작성했는데 안 올렸었다니..!!

 

공식문서를 보는 것으로 공부방향을 바꿨다.

https://react.dev/learn

 

Your First Component – React

The library for web and native user interfaces

react.dev

 



Q. 프로그래밍에서 동사 nest의 의미?

A.

프로그래밍에서 "nest"란, 하나의 코드 블록이 다른 코드 블록 안에 중첩되는 것을 의미합니다. 중첩된 블록은 다른 블록 내부에 들어가기 때문에 외부 블록의 범위를 포함하게 되며, 이러한 구조를 "nested" 구조 또는 "nesting"이라고 합니다.

대표적인 예시로는 if문이나 for문 같은 제어 구조가 있습니다. 이들 구조는 중첩될 수 있으며, 이를 통해 복잡한 조건과 반복 작업을 수행할 수 있습니다. 다른 예로는 함수 내에서 다른 함수를 호출하는 것도 있습니다. 이 때, 호출된 함수는 외부 함수의 범위 내부에 중첩됩니다.

 


Q. 프로그래밍에서 mark up something 이라는 표현은 무엇을 의미할까?

A.

프로그래밍에서 "mark up something"은, 일반 텍스트에 마크업 언어를 적용하여 의미론적 정보를 추가하는 것을 의미합니다. 이는 문서, 데이터 또는 다른 형태의 정보를 다룰 때 매우 유용합니다. 

마크업 언어를 사용하면 일반 텍스트에서 각 요소의 의미를 명확하게 표현할 수 있으며, 이는 정보를 더 잘 이해하고 처리할 수 있도록 도와줍니다. 예를 들어, HTML 마크업 언어를 사용하면 웹페이지의 구조, 내용, 레이아웃 등을 명확하게 표현할 수 있습니다. 

따라서 "mark up something"은 일반 텍스트에 의미론적 정보를 부여하여 보다 구체적이고 의미 있는 정보를 만드는 것을 의미합니다.


sprinkle: 간간히 섞다.


Q. snippet이 뭐야?

A.

컴퓨터 프로그래밍에서 "snippet"은 작은 코드 조각을 말합니다. 보통 코드의 재사용성을 높이기 위해 작성된 코드 조각이며, 특정 기능을 수행하는 코드 블록으로 구성될 수 있습니다. 

예를 들어, 웹 프로그래밍에서는 특정 웹 페이지에서 자주 사용되는 HTML, CSS, 자바스크립트 코드 조각을 "snippet"으로 작성하여 필요할 때 재사용할 수 있습니다. 이렇게 작성된 "snippet"은 코드의 재사용성을 높여 코드 작성 시간을 절약하고 코드의 일관성을 유지할 수 있도록 도와줍니다.

또한, 일부 IDE나 텍스트 에디터에서는 "snippet" 기능을 제공하여 자주 사용되는 코드 조각을 저장하고 검색하여 빠르게 삽입할 수 있도록 지원합니다. 이러한 기능은 개발자의 생산성을 높이는 데 매우 유용합니다.

 


1. Your first component

  • 리액트 컴포넌트의 이름들은 대문자로 무조건 시작해야 함.

App.js

import './App.css';

function Profile() {
  return (
    <img 
      src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
      alt='paris'
    />
  );
}

export default function Gallery() { //parent component, rendering each Profile as a child
  return (
    <section>
      <h1>Amazing Sites</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}

 

  • Never define a component inside another component
    • 컴포넌트들은 다른 컴포넌트를 render할 수는 있지만, 한 컴포넌트의 정의를 다른 컴포넌트 안에 nest할 수는 없음.
import './App.css';

export default function Gallery() { //parent component, rendering each Profile as a child
  function Profile() {
    return (
      <img 
        src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
        alt='paris'
      />
    );
  }

  return (
    <section>
      <h1>Amazing Sites</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}

이렇게 쓰지 말라는 건데 사실 이렇게 써도 돌아가긴 한다.

 

근데 위와 같이 쓰면 매우 느리고, 버그가 생긴다.

=> Define every component at the top level!

 

자식 컴포넌트가 부모로 부터 데이터를 받아와야 한다면, nesting child's definition을 하는 대신 props로 전달하자.

 

 

2. Importing and Exporting components

 

우리는 계속 App.js에서 작성을 했었다.

import './App.css';

function Profile() {
  return (
    <img 
      src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
      alt='paris'
    />
  );
}

export default function Gallery() { //parent component, rendering each Profile as a child
  return (
    <section>
      <h1>Amazing Sites</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}
  • 이 App.js가 root component file이다.
  • Create React App에서 우리의 앱은 src/App.js에 있다.
  • 그렇지만, setup에 따라 root componetn는 다른 파일에 있을 수도 있다
  • file-based routing을 사용하는 framework를 사용한다면 (ex. Next.js) root component가 페이지마다 달라질 수도 있다.

 

Profile과 Gallery 컴포넌트를 App.js에서 분리시켜보자. 파일 이름은 Gallery.js라 하자.

 

아래는 App.js

import Gallery from "./Gallery.js";

export default function App() {
  return (
    <Gallery />
  );
}

 

아래는 Gallery.js

function Profile() {
  return (
    <img 
      src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
      alt='paris'
    />
  );
}

export default function Gallery() { //parent component, rendering each Profile as a child
  return (
    <section>
      <h1>Amazing Sites</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}

분리하기 전과 동일하게 작동한다.

 

 


Default vs named exports

  • 한 파일은 최대 1개의 default export를 가질 수 있음.
  • 한 파일에는 원하는 만큼 많은 named exports를 가질 수 있음.
Syntax Export statement Import Statement
Default export default function Button() {} import Button from './Button.js';
Named export function Button() {} import {Button} from './Button.js';

 

  • default import 를 할 때, import 뒤에 원하는 이름을 넣을 수 있다.

ex) import Button from './Button.js'; 대신

import Bonobono from './Button.js'; 해도 됨.

 

이렇게 해줘도 똑같은 default export 를 제공함.

 

  • named import를 할 때, 컴포넌트의 이름은 양쪽(export 하는 파일, import하는 파일) 다 같아야 한다.

그래서 named import인 거다.

 

  • 이름이 없는 컴포넌트 예를 들면 arrow function
export default () => {}

은 권장되지 않는다.

디버깅을 더 어렵게 만들기 때문이다.

 

  • default와 named export의 잠재적인 혼란을 줄이기 위해 어떤 팀은 한 개의 파일에 default와 named 두개를 혼용해서 사용하는 것을 지양하는 편이다. 아예 한 스타일을 고수하는 방식으로

 

아래와 같은 방식도 당연히 가능하다.

/** App.js */
import Gallery from "./Gallery.js";
import {Profile} from "./Gallery.js";

export default function App() {
  return (
    <>
    <h1>Paris</h1>
    <Profile />
    </>
  );
  
}
/** Gallery.js */
export function Profile() {
  return (
    <img 
      src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
      alt='paris'
    />
  );
}

export default function Gallery() { //parent component, rendering each Profile as a child
  return (
    <section>
      <h1>Amazing Sites</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  )
}

 

 

3. Writing Markup with JSX

 

  • JSX: syntax extension for Javascript that lets you write HTML-like markup inside a JavaScript file
  • 대부분의 개발자들이 JSX의 간결성때문에 선호한다.
  • React에서는 rendering logic과 markup이 한 곳에 모여있다. => component
  • React component는 markup을 표현하기 위해 JSX라는 syntax extension을 사용한다.

 

  • JSX와 React는 서로 분리된 다른 것이다. 둘은 가끔 같이 사용될 수 있지만 각각 독립적으로도 사용이 가능하다.
  • JSX는 syntax extension
  • React는 Javascript library

 


Rules of JSX

① Return a single root element

  • single parent tag로 감싸기(ex. <div></div>)
/** App.js */
export default function App() {
  return (
    <div>
      <h1>Tina's Todos</h1>
      <img 
          src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
          alt='paris'
      />
    </div>
  )
}

 

  • <> and </>        => Fragment 
/** App.js */
export default function App() {
  return (
    <>
      <h1>Tina's Todos</h1>
      <img 
          src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
          alt='paris'
      />
    </>
  )
}

 

이 empty tag는 Fragment.

Fragment는 브라우저 HTML tree에 어떤 흔적도 남기지 않고 무언가를 grouping할 수 있게 해준다.

 

 

JSX 태그들이 wrap되어 하나로 묶여야하는 이유는

하나의 function에 두개의 객체들이 return될 수는 없기 때문이다.

 

 

② Close all the tags

 

JSX에서는 모든 태그들이 명시적으로 닫혀있어야 함. 

self-closing tag를 써서라도

 

 

③ camelCase most of the things

 


/** App.js */
export default function App() {
  return (
    <>
      <h1>Tina's Favorite Songs</h1>
      <img 
          src='https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg' 
          alt='paris'
      />
      <ul>
        <li>Le Festin</li>
        <li>La vien Rose</li>
        <li>L'Amour, Les Baguettes, Paris</li>
      </ul>
    </>
  )
}


challenge 1

export default function Bio() {
  return (
    <div class="intro">
      <h1>Welcome to my website!</h1>
      <p className="summary"> You can find my thoughts here.</p>
      <br></br>
      <b>And <i>pictures</i></b> of scientists!
    </div>
  );
}

 

 

4. Javascript in JSX with Curly Braces

Passing string with quotes

JSX에 string attribute를 전달하고 싶으면, '' 나 ""를 써주면 됨

/** App.js */
export default function Paris() {
  return (
    <img
      className="paris"
      src="https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg"
      alt = "Eiffel Tower"
    />
  );
}

 

"https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg" 나

"Eiffel Tower" 는 string으로 전달되었다.

 

그런데 만약에 src나 alt의 text 속성에 들어갈 text를 동적으로 명시하고 싶다면?

""를 {}로 바꿔주면 됨

/** App.js */
export default function Paris() {
  const pic = "https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg";
  const description = "Eiffel Tower"
  return (
    <img
      className="paris"
      src= {pic}
      alt = {description}
    />
  );
}

src={pic}은 pic이라는 javascript 변수의 값을 읽는다.

 

또 다른 예시를 보자.(예시2)

/** App.js */

export default function TodoList() {
  const name = 'Tina';
  return (
    <h1>{name}'s To Do List</h1>
  );
}

또 다른 예시를 보자.(예시 3)

/** App.js */
const today = new Date();
function formatDate(date) {
  return new Intl.DateTimeFormat(
    'en-US',
    {weekday:'long'}
  ).format(date);
}

export default function TodoList() {
  return (
    <h1>To Do List for {formatDate(today)}</h1>
  );
}

위의 코드는 React 컴포넌트에서 날짜를 포맷팅하여 출력하는 예시입니다.

`const today = new Date()` 코드는 현재 날짜와 시간을 나타내는 `Date` 객체를 생성합니다.

`formatDate(date)` 함수는 `Intl.DateTimeFormat` 생성자를 사용하여 날짜를 원하는 형식으로 포맷합니다. `Intl.DateTimeFormat` 생성자는 지정된 로케일에 따라 날짜와 시간을 형식화하는 객체를 반환합니다. 위의 예시에서는 미국 영어 로케일('en-US')을 사용하며, 'weekday' 옵션을 설정하여 요일 이름을 긴 형식으로 출력하도록 지정했습니다. `formatDate(today)` 코드는 `today` 변수에 저장된 현재 날짜를 `formatDate()` 함수를 통해 포맷팅한 결과를 반환합니다.

`export default function TodoList()` 코드는 React 함수형 컴포넌트를 정의합니다. 이 컴포넌트는 반환하는 JSX에서 `formatDate(today)`를 호출하여, 현재 날짜를 포맷팅한 결과를 출력합니다. 이러한 방식으로, 컴포넌트의 출력 결과는 현재 날짜와 요일을 포함하는 문자열이 됩니다.


Q. javascript에서 new는 뭐야?

A.

JavaScript에서 new 키워드는 새로운 객체를 생성하고, 해당 객체의 생성자 함수를 호출하여 객체를 초기화하는 역할을 합니다.

일반적으로 JavaScript에서 객체는 생성자 함수를 사용하여 생성됩니다. 생성자 함수는 일종의 템플릿 역할을 하며, 객체의 초기화를 담당합니다. new 키워드를 사용하여 생성자 함수를 호출하면, 새로운 객체가 생성되고, 생성자 함수가 실행됩니다. 이때 생성된 객체는 생성자 함수에서 반환된 객체이며, 해당 객체는 생성자 함수의 prototype 프로퍼티에 정의된 메소드와 속성을 상속받습니다.

new 키워드의 기본적인 사용법은 다음과 같습니다

function Person(name) {
  this.name = name;
}

const person = new Person('John');
console.log(person.name); // 'John'

예제에서 Person 함수는 생성자 함수입니다. new Person('John') Person 생성자 함수를 호출하여 새로운 Person 객체를 생성하고, 이를 person 변수에 할당합니다. 이때 this.name 생성된 객체의 프로퍼티로 설정되며, console.log(person.name) 'John' 출력합니다.

 


Where to use curly braces

    1.  As text direcly inside a JSX tag
<h1>To Do List for {name}</h1>

이렇게 쓰는 건 가능하지만 아래와 같이 쓰는 건 불가능하다.

<{tag}>To Do List for {name}</{tag}>

 

풀 예시

export default function TodoList() {
  const name = 'Tina';
  return (
    <h1>To Do List for {name}</h1>
  );
}

 

2. As attributes immediately following the = sign

<img
  className="paris"
  src= {pic}
  alt = {description}
/>

이렇게 써주면 src={pic} 은 pic이라는 변수를 읽을 것임.

 

풀 예시

export default function Paris() {
  const pic = "https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg";
  const description = "Eiffel Tower"
  return (
    <img
      className="paris"
      src= {pic}
      alt = {description}
    />
  );
}

 

 

Using 'double curlies' :  CSS and other objects in JSX

string, numbers, 그리고 이외의 JavaScript 표현들에 더하여 JSX에 객체들도 전달할 수 있다.

객체들은 아래와 같이 curly braces와 함께 표현된다.

{name : "Tina", inventions: 5 }

 

그래서 JS 객체들을 JSX에 전달하려면 curly braces로 한번 더 감싸주어야 한다. 아래처럼

person = {{ name:"Tina", inventions:0 }}

 

 

JSX에 inline CSS style을 적용하면 아래와 같다.

근데 CSS classes가 훨씬 좋기 때문에 아래의 방법이 잘 쓰이지는 않는다. (React inline style을 권장하지는 않음)

/** App.js */
export default function TodoList() {
  return (
    <ul style={{
      backgroundColor:'black',
      color:'pink'
    }}>
      <li>Homework</li>
      <li>Walk</li>
      <li>Sleep</li>
    </ul>
  )
}

 

한마디로

export default function TodoList() {

  return (

    <ul style={

      {

      backgroundColor:'black',

      color:'pink'

      }

    }>

      <li>Homework</li>

      <li>Walk</li>

      <li>Sleep</li>

    </ul>

  )

}

 

노란색으로 밑줄 친 부분이 object인 것이다.

 

JSX에서 {{와 }}가 나오면 JSX culies 안의 object로 무조건 생각하면 된다.

 

 

Curly braces를 이용해 Javascript Objects들 체험하기

/** App.js */
const person ={
  name: 'Tina',
  theme: {
    backgroundColor:'black',
    color:'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img 
        className="avatar"
        src = "https://media.triple.guide/triple-cms/c_fill,f_auto,h_256,w_256/9196dc1d-a76a-4988-8fc0-0f3ffc008b68.jpeg"
        alt = "Tina"
      />
      <ul>
        <li>Homework</li>
        <li>Walk</li>
        <li>Sleep</li>
      </ul>
    </div>
  )
}

person의 theme object가 style로 들어갔다.

 


challenge 1

const person = {
  name: 'Gregorio Y. Zara',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img
        className="avatar"
        src="https://i.imgur.com/7vQD0fPs.jpg"
        alt="Gregorio Y. Zara"
      />
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

 

challenge2

const person = {
  name: 'Gregorio Y. Zara',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  },
  url :"https://i.imgur.com/7vQD0fPs.jpg"
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img
        className="avatar"
        src= {person.url}
        alt="Gregorio Y. Zara"
      />
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

 

challenge3

못 풀어서 해답을 봤다.

 

Challenge 3 of 3: Write an expression inside JSX curly braces 

In the object below, the full image URL is split into four parts: base URL, imageId, imageSize, and file extension.

We want the image URL to combine these attributes together: base URL (always 'https://i.imgur.com/'), imageId ('7vQD0fP'), imageSize ('s'), and file extension (always '.jpg'). However, something is wrong with how the

tag specifies its src.

Can you fix it?

 

아래는 잘못된 코드

const baseUrl = 'https://i.imgur.com/';
const person = {
  name: 'Gregorio Y. Zara',
  imageId: '7vQD0fP',
  imageSize: 's',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img
        className="avatar"
        src="{baseUrl}{person.imageId}{person.imageSize}.jpg"
        alt={person.name}
      />
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

 

 

src="{baseUrl}{person.imageId}{person.imageSize}.jpg"

이 지점에서 문제가 생기는 건데

 

전체를 큰 curly brace로 감싸줘야 한다. 

src={baseUrl+person.imageId+person.imageSize+".jpg"}

요로케

 

그래서  고친 코드는 아래와 같다.

 

const baseUrl = 'https://i.imgur.com/';
const person = {
  name: 'Gregorio Y. Zara',
  imageId: '7vQD0fP',
  imageSize: 's',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img
        className="avatar"
        src={baseUrl+person.imageId+person.imageSize+".jpg"}
        alt={person.name}
      />
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

 

 

5. Passing props to a component

  • React component는 props를 사용해 서로 communicate한다.
  • 모든 부모 component는 자식 컴포넌트에게 props를 줌으로써 정보를 넘겨줄 수 있음.
  • object, array, functions 등등 모두 props가 될 수 있음.

 

<img> 태그에서 props를 전달하는 예

/** App.js */
function Avatar() {
  return (
    <img
      className="avatar"
      src="https://i.imgur.com/1bX5QH6.jpg"
      alt="Lin Lanying"
      width={100}
      height={100}
    />
  );
}

export default function Profile() {
  return (
    <Avatar />
  );
}

<img> 태그를 통해 전달할 수 있는 props들은 이미 정해져있다.

 

그러나 내가 만든 컴포넌트에서는 어떤 props든 전달할 수 있다.

해보자.

 

Passing props to a component

 

export default function Profile() {
  return (
    <Avatar />
  );
}

위의 코드에서는 Profile 컴포넌트가 그 자식 컴포넌트인 Avatar에 어떤 props도 전달하지 않고 있다.

 

두가지 스텝을 걸쳐 Avatar에 props를 전달해보자.

 

1. Pass props to the child component

먼저 Avatar에 몇개의 prop을 전달해보자.

여기선 person이라는 object와 size라는 number이 prop이다.

/** App.js */
function Avatar() {
  return (
    <img
      className="avatar"
      src="https://i.imgur.com/1bX5QH6.jpg"
      alt="Lin Lanying"
      width={100}
      height={100}
    />
  );
}

export default function Profile() {
  return (
    <Avatar 
      person = {{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
      size = {100}
    />
  )

 

2. Read props inside the child component

 

We can read these props by listing their names person, size

seperated by commas inside ({ and }) 

 

변수니까 {} 안에 넣어준다고 생각하면 편하다.

 

 

/** App.js */
import { getImageUrl } from "./utils";

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

export default function Profile() {
  return (
    <div>
      <Avatar 
        size={100}
        person = {{
          name: 'Katsuko Saruhashi',
          imageId:'Vfe0qp2'
        }}
      />
      <Avatar
        size={80}
        person = {{
          name :'Aklilu Lemma',
          imageId:'0KS67lh'
        }}
      />
      <Avatar 
        size = {50}
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />
    </div>
  );
}
/** utils.js */
export function getImageUrl(person, size='s') {
    return (
        'https://i.imgur.com/' +
        person.imageId +
        size +
        '.jpg'
    );
}

 

이미지가 업로드가 안 된다. 이미지 사이트에 변화가 있었던 것 같다.

그냥 넘어가자.

 

당연히 props가 하나일 수도 있다.

아예 객체 자체를 props로 받아와도 된다는 의미다.

/** App.js */
import { getImageUrl } from "./utils";
function Avatar(props) {
  let person = props.person;
  let size = props.size
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar 
        size={100}
        person = {{
          name: 'Katsuko Saruhashi',
          imageId:'Vfe0qp2'
        }}
      />
      <Avatar
        size={80}
        person = {{
          name :'Aklilu Lemma',
          imageId:'0KS67lh'
        }}
      />
      <Avatar 
        size = {50}
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />
    </div>
  );
}

이 전체 코드에서

function Avatar(props) {
  let person = props.person;
  let size = props.size

이 코드를 보자.

객체 전체를 props로 받아왔는데 대부분의 경우는 객체 전체가 필요하지는 않으므로 개별 props로 destructure 해주는 과정이 필요하다.

위의 코드가 바로 그렇다.

 

Specifying a default value for a prop

아무런 value가 명시되지 않을 때를 대비하여 default value를 prop에게 주고싶다면, 자식 컴포넌트에서 prop을 받을 때 =을 써주면 된다.

/** App.js */
import { getImageUrl } from "./utils";
function Avatar({person,size=100}) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar 
       //no size prop //default value used //size:100
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />

      <Avatar 
        size = {undefined} //default value used //size:100
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />

      <Avatar 
        size = {null} // default value not used //original img size
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />

      <Avatar 
        size = {500} // default value not used //size:500
        person = {{ 
          name: 'Lin Lanying', 
          imageId: '1bX5QH6' }}
      />
    </div>
  );
}

<Avatar person = {...} />

가 render 될 때, size 속성 없이 render 될 경우, size는 default인 100이다.

 

size 속성이 missing되거나 size={undefined}일 경우에만 default value가 사용된다.

그런데 만약 size={null}이거나 size={500}일 경우 default value가 사용되지 않는다. 

특이했던 건 size={null}이면 원본 이미지 사이즈가 렌더링 된다.

size={500}이면 지정된 500으로 이미지 사이즈가 적용된다.

 

Forwarding props with the JSX spread syntax

function Profile({ person, size, isSepia, thickBorder }) {
  return (
    <div>
      <Avatar 
        person={person}
        size = {size}
        isSepia = {isSepia}
        thickBorder = {thickBorder}
      />
    </div>
  )
}

위와 같이 props를 전달하는 과정이 굉장히 반복적일 수 있다.

이게 나쁘다는 건 아니다. 오히려 읽기 쉬울 수 있다.

 

그런데 만약 간결성을 원한다면 spread syntax를 사용할 수 있다.

function Profile(props) {
  return (
    <div>
      <Avatar 
        {...props}
      />
    </div>
  )
}

근데 이건 많~~~이 제한적일 듯 싶다.

 

Passing JSX as children

built-in broswer tag를 nest하면 다음과 같다. 

<div>
  <img />
</div>

 

근데 만약 내가 만든 컴포넌트를 nest하고 싶다면

<Card>
  <Avatar />
</Card>

이런 식이 될 것이다.

 

JSX 태그 안에 content를 nest하고 싶다면,

부모 컴포넌트는 children이라는 prop으로 그 content를 받게 된다.

 

아래의 예시에서 Card 컴포넌트는 children prop에 <Avatar />를 받게 된다.

/** App.js */
import Avatar from './Avatar.js';

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}
/** Avatar.js */
import { getImageUrl } from "./utils";

export default function Avatar ({person,size}) {
    return (
        <img 
            className="avatar"
            src={getImageUrl(person)}
            alt={person.name}
            width={size}
            height={size}
        />
    );
}
/** utils.js */
export function getImageUrl(person, size='s') {
    return (
        'https://i.imgur.com/' +
        person.imageId +
        size +
        '.jpg'
    );
}

children prop을 가진 컴포넌트를 이렇게 생각할 수도 있다.

구멍이 뚫려 있어서 자유롭게 JSX 형태의 것들이 들어갈 수 있다.

children prop은 visual wrapper로서 panels, grids 등등에 사용된다.

 

아래와 같이 <img> 태그로 채워줄 수도 있다.

/** App.js */
import Avatar from './Avatar.js';

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <img 
        src='https://www.publicdomainpictures.net/pictures/470000/nahled/paris-france-vintage-postcard.jpg'
        alt='Paris'
      />
    </Card>
  );
}

 

How props change over time

컴포넌트는 시간에 따라 다른 props를 전달받는다.

props는 언제나 정적이지 않다.

props는 매 시점의 component의 데이터를 반영한다.

 

하지만, props들은 immutable(=unchangable)하다

When a component needs to change its props, (ex. user interaction이나 new data에 대한 응답),

it will have to ask its parent componentn to pass it different props- a new object!

그러면 old props은 버려지고, Javascript engine will reclaim the memory taken by them.

 

change props하려고 하지 마라.

user input에 respond해야 한다면 set state를 사용하자.

 

 

**Props are read-only snapshots in time: every render receives new version of props

 

 

Challenge 1

/** App.js */
import { getImageUrl } from './utils.js';

function Profile({name,imageId,size,profession,awardNum,awards,discovered}) {
  return (
    <section className='profile'>
      <h2>{name}</h2>
      <img 
        className='avatar'
        src={getImageUrl(imageId)}
        alt={name}
        width={size}
        height={size}
      />
      <ul>
        <li>
          <b>Profession: </b>
          {profession}
        </li>
        <li>
          <b>Awards: {awardNum} </b>
          ({awards})
        </li>
        <li>
          <b>Discoverd: </b>
          {discovered}
        </li>
      </ul>
    </section>
  );
}

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <Profile 
        name = "Maria Skłodowska-Curie"
        imageId = 'szV5sdG'
        size = {70}
        profession = 'physicist and chemist'
        awardNum = {4}
        awards= "Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal"
        discovered= "polonium (element)"
      />
      
      <Profile 
        name = "Katsuko Saruhashi"
        imageId = 'YfeOqp2'
        size = {70}
        profession = 'geochemist'
        awardNum = {2}
        awards= "Miyake Prize for geochemistry, Tanaka Prize"
        discovered= "a method for measuring carbon dioxide in seawater"
      />
    </div>
  );
}

array를 써야할 거 같긴 한데 감이 안 와서 문자열들을 다 받아버렸다.

 

그래서 해답을 봤다.

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

function Profile({
  imageId,
  name,
  profession,
  awards,
  discovery,
  imageSize = 70
}) {
  return (
    <section className="profile">
      <h2>{name}</h2>
      <img
        className="avatar"
        src={getImageUrl(imageId)}
        alt={name}
        width={imageSize}
        height={imageSize}
      />
      <ul>
        <li><b>Profession:</b> {profession}</li>
        <li>
          <b>Awards: {awards.length} </b>
          ({awards.join(', ')})
        </li>
        <li>
          <b>Discovered: </b>
          {discovery}
        </li>
      </ul>
    </section>
  );
}

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <Profile
        imageId="szV5sdG"
        name="Maria Skłodowska-Curie"
        profession="physicist and chemist"
        discovery="polonium (chemical element)"
        awards={[
          'Nobel Prize in Physics',
          'Nobel Prize in Chemistry',
          'Davy Medal',
          'Matteucci Medal'
        ]}
      />
      <Profile
        imageId='YfeOqp2'
        name='Katsuko Saruhashi'
        profession='geochemist'
        discovery="a method for measuring carbon dioxide in seawater"
        awards={[
          'Miyake Prize for geochemistry',
          'Tanaka Prize'
        ]}
      />
    </div>
  );
}

역시 array를 썼다.

근데 생각보다 별거 없었다.

답을 안 보고 다시 한번 해봐야겠당

 

  • array를 넘겨줄 때 {} 안에 배열을 넣어야 함을 잊지 말자.
awards={[
      'Miyake Prize for geochemistry',
      'Tanaka Prize'
    ]}

요로케

 

 

Challenge 2

/** App.js */
import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  let thumbnailSize = 's';
  if (size>90) {
    thumbnailSize = 'b'
  };
  return (
    <img
      className="avatar"
      src={getImageUrl(person, thumbnailSize)}
      alt={person.name}
    />
  );
}

export default function Profile() {
  return (
    <>
    <Avatar
      size={40}
      person={{ 
        name: 'Gregorio Y. Zara', 
        imageId: '7vQD0fP'
      }}
    />
    <Avatar
      size={100}
      person={{ 
        name: 'Gregorio Y. Zara', 
        imageId: '7vQD0fP'
      }}
    />   
    </>
  );
}
/** utils.js */
export function getImageUrl(person, size) {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}

 

https://i.imgur.com/7vQD0fPs.jpg 에서는 사진이 작고

 

https://i.imgur.com/7vQD0fPb.jpg 에서는 사진이 크다.

 

 

Challenge 3

function Card({children}) {
return(
  <div className="card">
    <div className="card-content">
      {children}
    </div>
  </div>
);
}

export default function Profile() {
  return (
    <div>
      <Card>
      <h1>Photo</h1>
      <img
        className="avatar"
        src="https://i.imgur.com/OKS67lhm.jpg"
        alt="Aklilu Lemma"
        width={70}
        height={70}
      />
      </Card>
      <Card>
        <h1>About</h1>
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}

children 써줄 때

반드시 {} curly braces 써주기

 

6. Conditional Rendering

Conditionally returning JSX

컴포넌트들이 상황에 따라 다른 것들을 보여주야 할 때가 있다.

React에서는 if statement, &&, ? : 연산자들을 사용해 상황에 따라 JSX를 render할 수 있다.

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name} ✓</li> ;
  }
  return <li className="item">{name} </li> ; //위에 것이 return 되면 자동으로 이 코드는 실행이 안 되므로 else를 굳이 안 써줘도 됨
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true}
          name="Space suit"
        />
        <Item
          isPacked={false}
          name="Helmet with a golden leaf"
        />
        <Item
          isPacked={true}
          name="Photo of Tam"
        />
      </ul>
    </section>
  )
}

 

Conditionally returning nothing with null

아무것도 render하고 싶지 않을 수 있다.

컴포넌트는 반드시 무언가를 return 해야 하기 때문에 이 경우에 null을 return하면 된다.

function Item({name,isPacked}) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing list</h1>
      <ul>
        <Item
          isPacked={true}
          name="Space suit"
        />
        <Item
          isPacked={true}
          name="Helmet with a golden leaf"
        />
        <Item
          isPacked={false} //이것만 표시됨
          name="Photo of Tam"
        />
      </ul>
    </section>
  )
}

실제로, 컴포넌트에서 null을 return하는 일은 흔하지 않다.

조건적으로 부모 컴포넌트의 JSX에서 컴포넌트를 include하거나 exclude하는 것이 더 자주 사용된다.

 

Conditionally including JSX

1. Using Conditinal(ternary) Operator (? :)

if (isPacked) {
    return <li className="item">{name} ✓</li> ;
  }
  return <li className="item">{name} </li> ;

대신

return (
    <li className="item">
      {isPacked ? name + ' ✓' : name}
    </li>
);

? :를 사용하여 써줄 수 있다.

 

이 코드의 의미:

만약 isPacked가 true라면(then,?) name+' '✓ 를 render 해라

아니라면(otherwise, :) name을 render해라.

 


**두 코드는 완전 똑같은가?

객체 지향 프로그래밍을 배웠다면, 첫번째 코드가 두개의 다른 <li> instance를 생성하여 두 코드는 미묘하게 다른 것이라 추정할 수 있다.

근데 JSX elements들은 instance가 아니다.

 

 

'Front-End > React' 카테고리의 다른 글

[React] useMemo  (0) 2023.12.27
[React] 230501 학습일기  (0) 2023.05.01
[React] 220423 학습일기  (0) 2023.04.24
[React] 220416 학습일기  (1) 2023.04.17
[ReactJS] 코드 모음  (0) 2023.04.03