[react] practice

Published: by Creative Commons Licence

투투앱 만들기

구조 확인

html의 구조를 파악해서 컴포넌트로 나눌 단위를 정한다.

todoapp

컴포넌트 정의

컴포넌트는 자바스크립트 함수로 정의하면 되지만 함수와 아래 2가지가 다르다.

  • 컴포넌트명은 대문자로 시작
  • 리턴값은 JSX 마크업

컴포넌트 가져오기 내보내기

다른 곳에서 이 컴포넌트를 사용하려면 내보내기가 필요하다. default는 부모 컴포넌트 하나에만 붙일 수 있다.

export default function TodoApp() {
  // somethins to do
}
export function TodoItem() {
  // somethins to do
}

컴포넌트를 사용하기 위해선 가져오기가 필요하다. import를 붙이면 다른 컴포넌트나 모듈을 가져와 사용할 수 있다. export default된 컴포넌트는 가져온 쪽에서 원하는 이름을 붙일 수 있지만 default 없이 export만 있는 컴포넌트를 가져올때는 꼭 지정된 이름을 {}안에 넣어서만 사용할 수 있다.

import MyApp from './TodoApp.js'
import { TodoItem } from './TodoItem.js'

반환문에 JSX 적용하기

컴포넌트의 리턴값은 JSX여야 한다. 연관성이 있는 마크업과 로직을 한번에 렌더링 하기위해 사용하는 문법 확장프로그램이다.
HTML과 비슷하게 생겼지만 엄밀히 말해 HTML은 아니다 JSX는 자바스크립트 객체로 변환이 된다.

HTML문법과 비슷하게 사용할 수 있지만 다른 점은 닫는 태그가 꼭 존재해야 한다는 것. <input>태그를 보면 HTML에서는 없어도 되는 </>가 있다.

export default function TodoApp() {
  return (
    <div class="todo-input-box">
      <input class="todo-input" placeholder="할 일을 입력 후 엔터" />
    </div>
  );
}

App컴포넌트에서 나머지 컴포넌트를 중첩해서 사용한다.

import './Todo.css';

function Todos() {
  return (
    <ul class="todo-list">
      <Todo />
    </ul>
  );
}

function NewTodo() {
  return (
    <input type="text" class="todo-input" placeholder="할 일을 입력 후 엔터" />
  )
}


export default function TodoApp() { // 부모 컴포넌트
  return (
    <div class="todo-wrapper">
      <div class="todo-title">todos</div>
      <div class="todo-box">
        <div class="todo-input-box">
          <NewTodo />
        </div>
        <Todos />
      </div>
    </div>
  );
}

작은 단위로 HTML을 나눠 컴포넌트로 정의할 수 있다. 아직 기능을 붙이기 전이다.

import './Todo.css';

function Delete() {
  return (
    <button class="delBtn">x</button>
  );
}

function Content() {
  return (
    <div class="content">
      <input class="todo" />
    </div>
  );
}

function Check() {
  return (
    <div class="checkbox"></div>
  );
}

function Todo() {
  return (
    <li class="todo-item" >
      <Check />
      <Content />
      <Delete />
    </li>
  );
}

function Todos() {
  return (
    <ul class="todo-list">
      <Todo />
    </ul>
  );
}

function NewTodo() {
  return (
    <input type="text" class="todo-input" placeholder="할 일을 입력 후 엔터" />
  )
}


export default function TodoApp() { // 부모 컴포넌트
  return (
    <div class="todo-wrapper">
      <div class="todo-title">todos</div>
      <div class="todo-box">
        <div class="todo-input-box">
          <NewTodo />
        </div>
        <Todos />
      </div>
    </div>
  );
}

데이터 전달, props

props을 이용해 부모에서 자식컴포넌트로 데이터를 전달할 수 있다. 컴포넌트가 받는 유일한 인수(argument)는 props객체이다.
JSX문법 {}를 사용한다. 원시타입, 표현식, 함수, 배열, 객체, 이벤트 등 모든 타입의 데이터를 전달할 수 있다. {}내부는 자바스크립트 영역이라고 생각하면 된다.

속성도 {}로 전달할 수 있지만 스타일 속성은 ``를 사용해야 한다. {}안에 객체를 넘긴다고 생각하면 된다. 한가지 유의할 점은 JSX는 카멜케이스를 써야한다.

  • html: <ul style="background-color: black">
  • JSX: <ul style=>

부모컴포넌트에서 로컬스토리지에 있는 데이터를 조회해서 <Todos todos={todos} />로 자식컴포넌트에 전달한다.

import { useState, useEffect } from 'react'
import './Todo.css';
import NewTodo from './NewTodo'
import Todos from './Todos'

export default function TodoApp() {
  const [todos, setTodos] = useState(getStore());

  function getStore() {
    let todos = localStorage.getItem('todos');
    return JSON.parse(todos);
  }
  
  return (
    <div className="todo-wrapper">
      <div className="todo-title">todos</div>
      <div className="todo-box">
        <div className="todo-input-box">
          <NewTodo />
        </div>
        <Todos todos={todos} setTodos={setTodos} />
      </div>
    </div>
  );
}

props는 readonly 속성으로 리액트에서는 변경을 허용하지 않는다.

자식컴포넌트에서 전달받은 props의 변경이 필요하다면, 부모로부터 setState함수도 같이 전달을 받아 그것을 이용해 부모에 props의 변경을 알려야한다. 그래야 리렌더링이 진행이 된다. 그렇지 않을 경우 리렌더링은 일어나지 않아 화면에 해당 변경사항이 노출되지 않는다.

import TodoItem from './TodoItem';
import { setStore } from './store';

export default function Todos({ todos, setTodos }) {
  const todoList = todos.map(item => {
    return (
      <TodoItem todo={item} customUpdate={update} customDelete={deleteItem} key={item.seq} /> // map() 호출 안쪽에 꼭 key 추가해야함.
    )
  });
  function update() {
    setStore(todos)
    setTodos()
  }
  function deleteItem(targetSeq) {
    setStore(todos.filter(e => e.seq !== targetSeq));
    setTodos()
  }
  return (
    <ul className="todo-list">
      {todoList}
    </ul>
  );
}