중요! 부모 state 변경하기 (emit + v-model)Vue32022. 10. 19. 10:17
Table of Contents
emit
- emit은 데이터의 흐름이 prop과 다르다. emit은 자식 컴포넌트가 부모 컴포넌트에게 데이터를 보낸다.
- 부모 컴포넌트의 state를 prop으로 받고 자식 컴포넌트에서 emit으로 이벤트를 사용하면 two-way-binding!!
- 자식이 사용할 이벤트핸들러 function을 부모가 사용할 이름을 달아서 emit한 후 부모가 이 function을 커스텀해서 사용할 수 있다.
- 또한, 파라미터로 데이터를 넣어서 보내서 부모가 이 데이터를 사용해서 state를 변경하거나 사용할 수 있다!
사용법
자식 컴포넌트
- 자식 컴포넌트 methods 부분에 이벤트핸들러로 사용할 function을 작성한 후 emit 키워드를 사용한다. 이 때, 부모가 v-on으로 사용할 이벤트의 이름을 넣어준다!
- 자식 컴포넌트에서 html 태그에 이벤트 핸들러로 emit한 function을 적용시킨다
<template>
<!-- 이벤트 리스너 등록 -->
<button @click="setParentState">Change Parent State</button>
</template>
<script>
export default {
methods: {
setParentState: function () {
// 부모 컴포넌트가 사용할 이름=setState
this.$emit("setState");
},
},
};
</script>
- 아래처럼 inline으로 바로 emit을 사용할 수도 있다!
- emits 속성을 사용하지 않아도 정상적으로 작동하지만 부모 컴포넌트에서 코드를 입력할 때 자동완성도 안되고 어떤 emit인지 헷갈리기 때문에 나는 명시해서 쓰려고 한다!
<template>
<!-- 이벤트 리스너 등록 -->
<button @click="$emit('setState')">Change Parent State</button>
</template>
<script>
// emits 속성은 사용하지 않아도 정상적으로 작동!
export default {
emits: ["setState"],
};
</script>
부모 컴포넌트
- 부모는 v-on(@)를 이용해 자식이 emit한 function을 적어주고 이벤트 핸들러를 작성해서 사용한다!
<template>
<h1>{{ parentState }}</h1>
<!-- v-on, 자식이 넘긴 이름 사용 -->
<EmitChild @setState="setState" />
</template>
<script>
import EmitChild from "./components/EmitChild.vue";
export default {
name: "App",
data() {
return {
parentState: "Initial State",
};
},
components: { EmitChild },
methods: {
// 자식한 emit한 이벤트의 이벤트 핸들러 작성
setState: function () {
this.parentState = "Changed Parent State";
},
},
};
</script>
파라미터로 데이터를 넘길 때
- 자식 컴포넌트에서 emit을 이용해 데이터를 넘기면 부모 컴포넌트가 이를 받아 state를 바꾸는 등 여러가지 작업을 할 수 있다.
- 그리고 다른 결과를 뱉는 여러개의 function을 같은 이름으로 emit하면 자식 컴포넌트가 수행하는 function에 상관없이 부모 컴포넌트는 하나의 이벤트 핸들러로 자식이 처리해주는 여러가지 데이터를 받을 수 있다.
- 아래 예시를 한번 살펴봐보자!
자식 컴포넌트
- 자식 컴포넌트는 하나의 key에 여러가지 function을 emit해서 부모에게 전달한다.
- 부모의 상태를 props로 받아서 부모의 상태에 각각 +1, -1, *2을 해준다.
<template>
<!-- 이벤트 리스너 등록 -->
<div>
<button @click="plus_one">Parent state ++</button>
<button @click="minus_one">Parent state --</button>
<button @click="multiply_two">Parent state * 2</button>
</div>
</template>
<script>
export default {
props: {
num: Number,
},
methods: {
// 하나의 key로 여러가지 function 생성
plus_one: function () {
// 부모 컴포넌트가 사용할 이름=setState
this.$emit("setState", this.num + 1);
},
minus_one: function () {
this.$emit("setState", this.num - 1);
},
multiply_two: function () {
this.$emit("setState", this.num * 2);
},
},
};
</script>
- 아래처럼 inline으로 부모 컴포넌트가 사용할 이벤트 이름과 데이터를 넘겨줄 수도 있다!
<template>
<!-- 이벤트 리스너 등록 -->
<div>
<button @click="$emit('setState', num + 1)">Parent state ++</button>
<button @click="$emit('setState', num - 1)">Parent state --</button>
<button @click="$emit('setState', num * 2)">Parent state * 2</button>
</div>
</template>
<script>
export default {
props: {
num: Number,
},
emits: ["setState"],
};
</script>
부모 컴포넌트
- 부모 컴포넌트는 자식 컴포넌트가 넘겨준 이벤트를 이용해 자신의 상태에 각각 +1, -1, *2를 해준 값으로 자신의 상태를 변경한다.
- 자식이 파라미터로 데이터를 넘겨줬으므로 value라는 파라미터를 이용해 자신의 상태를 업데이트한다!
<template>
<h1>{{ parentState }}</h1>
<!-- v-on, 자식이 넘긴 이름 사용 -->
<EmitChild @setState="setState" :num="parentState" />
</template>
<script>
import EmitChild from "./components/EmitChild.vue";
export default {
name: "App",
data() {
return {
parentState: 0,
};
},
components: { EmitChild },
methods: {
// 자식한 emit한 이벤트의 이벤트 핸들러 작성
setState: function (value) {
// 자식이 넘겨준 데이터로 상태 업데이트
this.parentState = value;
},
},
};
</script>
input으로 부모 state 바꾸기 (잘못된 방법!!)
- 내가 이 방법으로 자식의 input으로 부모의 state를 변경하려 했었다...
부모 컴포넌트
<template>
<h1>{{ state }}</h1>
<TestVue :state="state" />
</template>
<script>
import TestVue from "./components/TestVue.vue";
export default {
data() {
return {
state: "",
};
},
components: { TestVue },
name: "App",
};
</script>
자식 컴포넌트
<template>
<span>Parent Input</span
><input v-model="state" />
</template>
<script>
export default {
props: {
state: String,
},
};
</script>
- 하지만 아래와 같은 에러가 발생했다
- Unexpected mutation of "state" prop.
- 자식 컴포넌트에서 자기의 input에 부모의 상태를 binding해서 prop을 바꾸려 했기 때문이다!
- 부모의 상태를 바꾸고 싶다면 부모가 prop으로 내려준 자신의 상태를 변경시키는 함수를 이벤트 핸들러로 사용하거나 emit을 이용해 부모한테 요청을 해야한다!
- 한마디로 부모의 state를 prop으로 받아서 v-model을 사용할 수 없다!!
input으로 부모의 상태 바꾸기(emit)
- 자식 컴포넌트에서 prop으로 부모의 상태를 받아서 v-bind로 input에 바인딩하고 이벤트 핸들러로 input의 값을 파라미터로 가지는 함수를 emit한다.
- 부모 컴포넌트에서는 자식 컴포넌트가 emit을 하면서 넘겨준 함수를 v-on으로 묶어주고 이벤트 핸들러를 methods에 정의하고 state를 파라미터로 바꿔준다!
자식 컴포넌트
- input의 값을 넣어서 emit하기!
<template>
<span>Parent Input</span
><input :value="state" @input="$emit('setState', $event.target.value)" />
</template>
<script>
export default {
props: {
state: String,
},
emits:[
"setState"
]
};
</script>
부모 컴포넌트
- 자식이 emit한 함수를 v-on으로 묶고 state를 파라미터로 바꾸는 함수를 정의해서 이벤트 핸들러로 사용하기!
<template>
<h1>{{ state }}</h1>
<TestVue :state="state" @setState="setState" />
</template>
<script>
import TestVue from "./components/TestVue.vue";
export default {
name: "App",
data() {
return {
state: "",
};
},
components: { TestVue },
methods: {
setState(value) {
this.state = value;
},
},
};
</script>
- 자식의 input이 부모의 state를 바꾸는 모습이다!
input으로 부모의 상태 바꾸기(emit + v-model)
- v-model = prop + emit
2-way-binding
- v-model을 이용해 쉽게 부모 컴포넌트와 자식 컴포넌트를 2-way-binding을 할 수 있다!
- 어떠한 컴포넌트 내에서 input과 data(state)의 2-way-binding은 HTML(view)과 JS의 데이터(state)를 동기화 시켜주는 것이지만 Vue.js에서 컴포넌트 사이의 2-way-binding은 부모 컴포넌트와 자식 컴포넌트 사이의 데이터(부모의 state)를 동기화 시켜주는 것이다!
- 자식의 input과 부모의 state가 같아짐!
사용법
- 부모 컴포넌트에서 2-way-binding할 상태를 아래와 같은 형식으로 자신의 state를 넘겨준다!
- v-model뒤에 :[자식이 사용할 prop 명]으로 붙여주는데 만약 붙여주지 않는다면 modelValue라는 디폴트 이름을 사용한다!
<TestVue v-model:state="state" />
- 자식 컴포넌트에서는 prop으로 부모가 넘겨준 prop을 v-bind로 바인딩하고 update:[prop 이름]으로 emit해주면 부모와 자식 사이의 2-way-binding이 설정된다!
<input :value="state" @input="$emit('update:state', $event.target.value)" />
전체 코드
부모 컴포넌트
- 자신의 state를 v-model로 자식 컴포넌트에 넣는다
<template>
<h1>{{ state }}</h1>
<TestVue v-model:state="state" />
</template>
<script>
import TestVue from "./components/TestVue.vue";
export default {
name: "App",
data() {
return {
state: "",
};
},
components: { TestVue },
};
</script>
자식 컴포넌트
- input에 부모의 state를 v-bind로 엮어주고 update:[prop 이름]으로 emit해준다!
<template>
<span>Parent Input</span
><input :value="state" @input="$emit('update:state', $event.target.value)" />
</template>
<script>
export default {
props: {
state: String,
},
emits: ["update:state"],
};
</script>
'Vue3' 카테고리의 다른 글
vue-router (0) | 2022.10.20 |
---|---|
Slot (0) | 2022.10.20 |
부모 state 변경하기 (prop) (0) | 2022.10.17 |
Array 렌더링 (v-for) (0) | 2022.10.17 |
조건부 렌더링 (v-if/v-else/v-else-if/v-show) (1) | 2022.10.17 |
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!