지난 포스팅에서는 기본 컴포넌트를 구성해보았습니다. 이번 시간에는 recoil
을 사용하여 기본 todo 아이템들을 뿌려주는 것까지 만들어보겠습니다.
Header
Main
Footer
Info
를 App
에 포함하도록 합니다.
// src/App.js
import Header from './components/Header'
import Main from './components/Main'
import Footer from './components/Footer'
import Info from './components/Info'
function App() {
return <>
<section className="todoapp">
<Header />
<Main />
<Footer />
</section>
<Info />
</>
}
export default App
각 컴포넌트들을 index.js
파일로 생성했기 때문에 디렉터리 경로까지만 import
를 하여도 기본값으로 index.js
를 가져옵니다.
현재까지의 결과화면은 아래와 같습니다.
지금까지 기본 화면을 구성하였습니다. 이제 todo
아이템들을 보여주는 것으로 구현을 시작해보도록 하겠습니다.
Todo
컴포넌트 분리TODO
아이템들은 Main
컴포넌트에 존재하므로 Main
컴포넌트를 열어줍니다.
// src/components/Main/index.js
function Main() {
return (
<section className="main">
<input id="toggle-all" className="toggle-all" type="checkbox" />
<label htmlFor="toggle-all">Mark all as complete</label>
<ul className="todo-list">
<li className="completed">
<div className="view">
<input className="toggle" type="checkbox" checked />
<label>Taste JavaScript</label>
<button className="destroy" />
</div>
<input className="edit" value="Create a TodoMVC template" />
</li>
</ul>
</section>
)
}
export default Main
ul
안에 li
가 존재합니다. 각 li
가 하나의 TODO를 표현하기 때문에, 얘를 컴포넌트로 빼주도록 하겠습니다. src/components/Main/Todo.js
를 하나 새로 만듭니다.
// src/components/Main/Todo.js
function Todo(props) {
return (
<li className="completed">
<div className="view">
<input className="toggle" type="checkbox" checked />
<label>Taste JavaScript</label>
<button className="destroy" />
</div>
<input className="edit" value="Create a TodoMVC template" />
</li>
)
}
export default Todo
Todo
컴포넌트의 데이터는 Main
에서 뿌려주기 때문에, props
로 받습니다.
참고로
recoil
은react hook
을 기반으로 동작하기 때문에, Class 컴포넌트가 아닌 Function 컴포넌트들로 구성하도록 합니다.
Todo
컴포넌트의 루트 태그인 li
의 클래스에는 editing
과 completed
두 개의 의미있는 클래스가 todomvc-app-css
에 정의되어 있습니다. editing
의 경우 Todo
를 더블클릭했을 때 내용을 수정할 수 있는 input
창이 보여지며 completed
는 완료된 표시로 스타일이 나오도록
정의되어있습니다.
editing
기능은 마지막에 구현하도록 하고, 우선 Todo
의 내용을 표시할 todo
데이터를 정의해보도록 하겠습니다.
atom
이제 recoil
이 등장할 때입니다. recoil
의 메인 페이지에서는 recoil
을 아래와 같이 정의하고 있습니다.
그저 React의 state
를 관리하는 라이브러리입니다. 따라서 어떤 상태, 즉 데이터들이 있고 이를 쉽게 관리해주는 라이브러리라고 생각하면 됩니다.
우리는 todos의 상태를 먼저 정의할 겁니다. src/state
디렉터리를 생성하고 하위에 todos.js
파일을 아래와 같이 생성합니다.
// src/state/todos.js
import { atom } from 'recoil'
let uniqId = 0
export const createTodo = text => ({
id: ++uniqId,
done: false,
text,
})
export const todos = atom({
key: 'todos',
default: [
createTodo('react 공부하기'),
createTodo('recoil 공부하기'),
],
})
하나하나 살펴보도록 하겠습니다.
import { atom } from 'recoil'
// ...
export const todos = atom({
key: 'todos',
default: [
createTodo('react 공부하기'),
createTodo('recoil 공부하기'),
],
})
atom
은 어떤 하나의 상태를 정의하는 recoil
함수입니다. key
는 모든 recoil
상태들 중 유일한 값이어야 하며, default
로 기본값을 정의할 수 있습니다.
let uniqId = 0
export const createTodo = text => ({
id: ++uniqId,
done: false,
text,
})
React에서는 같은 컴포넌트를 여러개 뿌려줄 때 key
속성이 필요합니다. 이 key
는 형제들중 유일한 값이어야 하는데, 따라서 새로운 Todo
가 생성될 때마다 uniqId
값을 이용해 유일한 값을 만들어 줄겁니다.
이를 위해 새로운 todo를 생성하는 createTodo
함수를 만들고, 이 함수로 todo
객체를 생성하도록 합니다. 이 때 ++uniqId
로 id
값이 유일하도록 보장하도록 합니다.
이제 정의된 todos
state를 Main
컴포넌트에서 가져와서 사용하겠습니다.
useRecoilValue
useRecoilValue
에서 use
prefix는 react hook의 그 use
와 같은 맥락입니다. 일반적으로 react hook을 살펴보면
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
// ...
}
위와 같이 기본값 0
을 정의하면서 useState
로 컴포넌트에 훅을 걸어줍니다. 따라서 setCount
가 호출될 때마다 해당 컴포넌트가 값의 변화를 인지하여 리렌더링 되도록 도와줍니다.
recoil
역시 마찬가지입니다. 우선 값인 state
를 정의하고(위의 경우 atom
으로 정의), 이를 useRecoilValue
로 컴포넌트에 훅을 걸어줍니다.
따라서, react hook과 마찬가지로 useRecoilValue
역시 리액트 컴포넌트 안에서 호출되어야 합니다.
이를 적용하여 만든 Main
컴포넌트는 아래와 같습니다.
// src/components/Main/index.js
import { useRecoilValue } from 'recoil'
import * as state from '../../state/todos'
import Todo from './Todo'
function Main() {
// state.todos는 atom으로 정의된 state입니다.
// useRecoilValue는 이 state의 변화를 Main 컴포넌트가 감지할 수 있도록 hook을 겁니다.
const todos = useRecoilValue(state.todos)
const Todos = todos.map(todo => <Todo key={todo.id} todo={todo} />)
return (
<section className="main">
<input id="toggle-all" className="toggle-all" type="checkbox" />
<label htmlFor="toggle-all">Mark all as complete</label>
<ul className="todo-list">
{ Todos }
</ul>
</section>
)
}
export default Main
현재 todos에는 2개의 값이 들어있으므로 아래와 같은 화면이 나올 것입니다.
이제 다시 Todo
컴포넌트로 돌아가서, 전달받은 props
를 뿌려주도록 수정하면 되겠습니다.
// src/components/Main/Todo.js
function Todo(props) {
const { id, done, text } = props.todo
return (
<li className={ done ? 'completed' : '' }>
<div className="view">
<input className="toggle" type="checkbox" checked={ done } />
<label>{ text }</label>
<button className="destroy" />
</div>
<input className="edit" value="Create a TodoMVC template" />
</li>
)
}
export default Todo
그러면 이제 아래와 같이 나옵니다.
todos를 뿌려주는 것까지 해봤습니다. recoil
은 react hook을 바탕으로 만들어진 라이브러리이기 때문에 상당히 심플합니다. 지금까지 배운 recoil
내용을 정리해보면
atom
으로 상태를 정의합니다.useRecoilValue
로 atom
을 구독합니다.끝입니다. 참 쉽죠?
다음 시간에는 selector
와 useRecoilState
를 이용하여 All, Active, Completed 등 상태에 따른 todos 목록 보여주는 기능을 구현해보도록 하겠습니다.