DevYoon
[Vue] Vuex를 이용하여 TodoList App을 만들어 보자 ⚒️ 본문
1️⃣ Store
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
plugins:[
createPersistedState()
],
state: {
todos:[
]
},
getters: {
isdone(state){
return state.todos.filter(todo => {
return todo.done === true
}).length
},
isnotyet(state){
return state.todos.filter(todo => {
return todo.done === false
}).length
},
allcount(state){
return state.todos.length
}
},
mutations: {
CREATE_TODO(state, todoItem){
state.todos.push(todoItem)
},
DELETE_TODO(state, todoItem){
const todoIndex = state.todos.indexOf(todoItem)
state.todos.splice(todoIndex, 1)
},
UPDATE_TODO(state, todoItem){
state.todos = state.todos.map(todo=>{
if(todo===todoItem){
return {
...todo,
done : !todo.done
}
}else{
return todo
}
})
},
},
actions: {
createTodo({commit}, todoItem){
commit('CREATE_TODO', todoItem)
},
deleteTodo(context, todoItem){
context.commit('DELETE_TODO', todoItem)
},
updateTodo(context, todoItem){
context.commit('UPDATE_TODO', todoItem)
}
},
modules: {
}
})
1️⃣ vuex-persistedstate
- Vuex의 state에 저장된 값을 웹 브라우저의 localStorage에 저장 및 업데이트
- 새로고침 후에도 localStorage에서 값을 불러올 수 있음
2️⃣ App
<template>
<div id="app">
<h1><i>ToDoList</i></h1>
<i class="fa-solid fa-check"></i>
<span><b>All</b> : {{allcount}}</span>
<br>
<i class="fa-solid fa-check"></i>
<span><b>Completed</b> : {{isdone}}</span>
<br>
<i class="fa-solid fa-check"></i>
<span><b>Not yet</b> : {{isnotyet}}</span>
<todo-form></todo-form>
<todo-list></todo-list>
</div>
</template>
<script>
import TodoList from './components/TodoList.vue'
import TodoForm from './components/TodoForm.vue'
import {mapGetters} from 'vuex'
export default {
name: 'App',
components: {
TodoList,
TodoForm
},
computed:{
...mapGetters(['isdone', 'isnotyet', 'allcount'])
// isdone(){
// return this.$store.getters.isdone
// },
// isnotyet(){
// return this.$store.getters.isnotyet
// },
// allcount(){
// return this.$store.getters.allcount
// }
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin: 60px auto;
width: 70%;
padding:30px;
border-radius:20px;
box-shadow: 1px 1px 3px 1px #dadce0 inset;
background-color:white;
}
body{
background-color: #F7F7F7;
}
h1{
margin-bottom: 50px;
}
#app span{
margin-left:10px;
}
#app .fa-solid{
margin-bottom:20px;
color:lightsalmon
}
</style>
3️⃣ TodoForm
<template>
<div class='TodoForm' @click='FormClick'>
<h2>오늘의 할 일은?</h2>
<input type="text" v-model.trim="todoTitle" @keyup.enter="createTodo" ref='Input'>
<button @click="createTodo" class='addTodo'>add</button>
</div>
</template>
<script>
export default {
name:'TodoForm',
data(){
return{
todoTitle:''
}
},
methods:{
createTodo(){
const todoItem={
title:this.todoTitle,
done:false,
date:new Date().getTime()
}
if(this.todoTitle){
this.$store.dispatch('createTodo', todoItem)
}
this.todoTitle = ''
},
FormClick(){
this.$refs.Input.focus();
}
}
}
</script>
<style>
.TodoForm{
/* border:2px solid lightsteelblue;
padding:20px; */
margin:50px;
}
.addTodo{
background-color: lightsalmon;
border:0px;
border-radius:5px;
color:white;
padding:5px 10px;
}
input{
width:150px;
height:30px;
}
</style>
4️⃣ TodoList
<template>
<div class='TodoList'>
<todo-list-item v-for="todo in todos" :key="todo.date" :todo="todo"/>
</div>
</template>
<script>
import TodoListItem from './TodoListItem.vue'
import {mapState} from 'vuex'
export default {
name:'TodoList',
components:{
TodoListItem
},
computed:{
...mapState(['todos']),
// todos(){
// return this.$store.state.todos
// }
}
}
</script>
<style>
.TodoList{
/* border: 2px solid orange;
padding: 30px; */
margin: 50px;
}
</style>
5️⃣ TodoListItem
<template>
<div class='TodoListItem'>
<span @click='updateTodo(todo)' v-bind:class="{'is-done': todo.done}">{{todo.title}}</span>
<button @click='deleteTodo(todo)' class='deleteButton'>X</button>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name:'TodoListItem',
props:{
todo:{
type:Object
}
},
methods:{
...mapActions(['deleteTodo', 'updateTodo']),
// deleteTodo(){
// this.$store.dispatch('deleteTodo', this.todo)
// },
// updateTodo(){
// this.$store.dispatch('updateTodo', this.todo)
// }
},
create(){
console.log(this.$store.getters)
}
}
</script>
<style>
.TodoListItem span{
display:inline-block;
background-color:lightsteelblue;
border-radius:20px;
margin-top: 25px;
padding: 15px 20px;
cursor:pointer;
box-shadow: 1px 1px 5px 1px #7A96B2 inset;
color:#2c3e50;
}
button{
margin-left:15px;
}
.deleteButton{
background-color: transparent;
border:0px;
color:lightsalmon;
}
.is-done{
text-decoration:line-through;
}
</style>