最近Nuxt.jsを使ってサービスの開発をしてみてます。
フロントエンドのフレームワークの経験がなく慣れておらず、
状態管理でハマったところがあったので備忘録として投稿。
個人的にはてなブログにかいてましたが
技術系はQiitaに書こうと決めたので内容は下記の書き直しとなります。
https://moritomo7315.hatenablog.com/entry/vuex/state_manage/1
ちなみにログイン認証等は今回の記事の本質とは離れてるので割愛してます。
何にハマったか
userのログイン・ログアウトをVuexで
// login
loginStatus = true
// logout
loginStatus = false
のように状態管理しようとしたわけですが、下記のような現象が起きてしまいました。
- ログアウトする場合:loginStatusがtrue->falseに変化
- ログインする場合:loginStatusがfalseのまま
ソースコード
store
import Vuex from 'vuex'
import user from './modules/user'
export default () => new Vuex.Store({
modules: {
user
}
})
const state = {
user: null,
loginStatus: false
}
const getters = {
user: (state) => state.user,
isLogin: (state) => state.loginStatus
}
const mutations = {
setUser(state, { user }) {
state.user = user
},
login(state) {
state.loginStatus = true
},
logout(state) {
state.loginStatus = false
state.user = null
}
}
const actions = {
fetchUser({ commit }, user) {
commit('setUser', {user})
},
login({ commit }) {
commit('login')
},
logout({ commit }) {
commit('logout')
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
header コンポーネント
<template>
<div>
<v-app-bar
color="primary"
>
<v-toolbar-title>
<nuxt-link to='/'>
<img src="~/static/weblogo.png">
</nuxt-link>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-btn
icon
color="transparent"
v-on="on"
>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
</v-btn>
</template>
<v-list v-if="loginStatus">
<v-list-item
nuxt
to='#'
>
<v-list-item-title>マイページ</v-list-item-title>
</v-list-item>
<v-list-item
@click="logout"
nuxt
to='/'
inactive
>
<v-list-item-title>ログアウト</v-list-item-title>
</v-list-item>
</v-list>
<v-list v-else>
<v-list-item
nuxt
to='/login'
>
<v-list-item-title>ログイン</v-list-item-title>
</v-list-item>
<v-list-item
nuxt
to='/signup'
>
<v-list-item-title>会員登録</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-app-bar>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
export default {
name: "Header",
computed: {
...mapState({
user: state => state.user.user,
loginStatus: state => state.user.loginStatus
})
},
methods: {
...mapActions('user',[
"logout"
])
}
}
</script>
login page
<!-- EmailSigninコンポーネントを呼び出してるだけ。今後Twitterログイン等も入れたいので、コンポーネントに分けている -->
<template>
<EmailSignin />
</template>
<script>
import EmailSignin from '~/components/emailSignin'
export default {
components: {
EmailSignin
}
}
</script>
<template>
<div class="userpage">
<h1>ログイン</h1>
<div class="userform">
<v-form>
<v-text-field
v-model="email"
label="E-mail"
outlined
required
></v-text-field>
<v-text-field
v-model="password"
label="password"
outlined
type="password"
required
></v-text-field>
<v-btn
color="secondary"
class="mr-4"
@click="login"
x-large
nuxt
to="/"
>
ログイン
</v-btn>
<nuxt-link
to="#"
>
パスワードを忘れた方はこちら...
</nuxt-link>
</v-form>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from "vuex";
export default {
name: "EmailSignin",
data() {
return {
email: '',
password: '',
}
},
computed: {
...mapState({
user: state => state.user.user,
loginStatus: state => state.user.loginStatus
})
},
methods: {
...mapActions('user',[
"login"
]),
passworLogin () {
this.login()
}
}
}
</script>
何が問題だったか
結論から言うと、
@click
の使い方が原因でした。
ソースコードでいうと、storeの記述に関しては問題なしです。
なぜログアウトはできて、ログインができなかったか
なぜログアウトはできるのに、
ログインはできないといった現象が起きてしまっていたかと言うと、
これは親子コンポーネントとイベントの関係があるみたいです。
僕のソースコードで実装されてるログアウトとログインの違いは
- headerコンポーネントがコンポーネント内のアクション
logout
を呼びイベント発生 - loginコンポーネントがemailSigninコンポーネント内の
login
を呼びイベント発生
となっております。
どちらも@click="methodName
としていますが、
ログインだけ動かないのはコンポーネントの親子関係が原因でした。
親コンポーネントに子コンポーネントのアクションイベントを実行させるには、
@click.native="methodName
と指定する必要があるみたいです。
まとめ
コンポーネントの親子関係を意識して、
そのコンポーネントが親か子に応じて
@click
か@click.native
を指定するで解決しました。
まだvuexの概念を理解しきっていないので、
説明に使用してる言葉があやしいところもあるかもしれませんが、
無事解決できました。