Todo list예제를 통한 redux학습하기(3)

redux를 통한 Todo list react앱을 만들기에 앞서 redux 개념을 학습하고 간단한 counter앱을 통해 redux동작 원리를 이해하는 시간을 가졌다. 이제는 본격적으로 todo list앱을 만들어보려고 한다.

먼저 counter앱을 만든 구조를 그대로 활용해서 todo를 추가하고 todo list를 보여주는 기능을 만드려고 한다.

먼저 index.html은 이전과 같이 root id를 가진 div를 생성해준다.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redux Todo</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

action은 새로운 todo를 추가해주는 ‘ADD_TODO’라는 action을 만들어 줄 것이다. counter 앱과 다른 점이라면 추가 할 todo의 내용이 들어가야 하기 때문에 counter의 increase, decrease action과 달리 text를 받아 타입과 함께 전달해 주어야 한다는 점이다.

//actions/index.jsexport const ADD_TODO = 'ADD_TODO';

export function addTodo(text) {
return { type: ADD_TODO, text }
};

components는 우선 세 가지를 만들었다.

각각의 todo를 표현하는 Todo component, todo들을 담고 있는 TodoList component, 새로운 todo를 추가하는 AddTodo component이다.

//components/Todo.jsimport React from 'react';

const Todo = ({ text }) => (
<li>{text}</li>
);

export default Todo;

Todo의 경우 지금은 단순히 li태그에 감싸진 텍스트이지만, 앞으로 삭제나 완료 기능 등이 추가될 것이다.

//components/TodoList.jsimport React, {Component} from 'react'

import Todo from './Todo';

class TodoList extends Component {
constructor(props) {
super(props);
}

render() {
const { todos } = this.props;
const todoEls = todos.map((todo, index) => (
<Todo key={index} {...todo} />
));

return (
<ul>{todoEls}</ul>
);
}
}

export default TodoList;

React Component인 TodoList는 state에 있는 todos를 전달받아 todo list를 만들고, 이를 반환한다.

//components/AddTodo.jsimport React, {Component} from 'react'

class AddTodo extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}

componentDidMount() {
this.inputTodo = document.querySelector('#inputTodo');
}

onClick(e) {
e.preventDefault();
const { addTodo } = this.props;
addTodo(this.inputTodo.value);
this.inputTodo.value = '';
}

render() {
return (
<form id='addTodo'>
<input id='inputTodo' placeholder='Enter todo'/>
<button type='submit' onClick={this.onClick}>ADD</button>
</form>
);
}
}

export default AddTodo;

AddTodo에서는 store에 dispatch해주는 addTodo function을 전달받아 onClick event를 만드는데 사용했다. inputTodo에 있는 값을 addTodo의 parameter로 전달한다. 마지막으로 Todo가 추가되면 input값은 초기화시켜준다.

reducer에서는 ADD_TODO action을 전달받으면 state를 변경해준다.

// reducers/index.jsimport { ADD_TODO } from "../acitons";

export default (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
id: action.id,
text: action.text
},
];
default:
return state;
}
}

…state는 state내부 값을 모두 가져와주고, 거기에 새로 추가 된 todo 객체를 포함시켜 새 배열을 만들어 반환했다.

import { createStore } from 'redux';
import React from 'react';
import ReactDOM from 'react-dom'

import rootReducer from './reducers';
import { addTodo } from "./acitons";
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';

const store = createStore(rootReducer);

const render = () => ReactDOM.render(
<div id='app'>
<AddTodo addTodo={(text) => { store.dispatch(addTodo(text)) }}/>
<TodoList todos={store.getState()}/>
</div>
,
document.querySelector('#root')
);

render();
store.subscribe(render);

마지막으로 root dive에 앱을 랜더링 해주고 store를 생성해줄 index.js를 작성한다.

//index.jsimport { createStore } from 'redux';
import React from 'react';
import ReactDOM from 'react-dom'

import rootReducer from './reducers';
import { addTodo } from "./acitons";
import TodoList from './components/TodoList';
import AddTodo from './components/AddTodo';

const store = createStore(rootReducer);

const render = () => ReactDOM.render(
<div id='app'>
<AddTodo addTodo={(text) => { store.dispatch(addTodo(text)) }}/>
<TodoList todos={store.getState()}/>
</div>
,
document.querySelector('#root')
);

render();
store.subscribe(render);

index.js에서는 reducer를 통해 store를 생성하고, AddTodo와 TodoList를 렌더링한다. AddTodo에는 ‘ADD_TODO’ type과 text를 가지고 있는 action을 만들어 dispatch시키는 함수를 넘겨준다.

이렇게 실행시켜주면

완성!

Github Source Code

woohyun