1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Vuex, Nuxt.js】子コンポーネントのアクションを実行しても状態が反映されない

Posted at

最近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

index.js
import Vuex from 'vuex'
import user from './modules/user'

export default () => new Vuex.Store({
  modules: {
    user
  }
})
user.js
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 コンポーネント

header.vue
<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

login.vue
<!-- EmailSigninコンポーネントを呼び出してるだけ今後Twitterログイン等も入れたいのでコンポーネントに分けている -->
<template>
  <EmailSignin />
</template>

<script>
import EmailSignin from '~/components/emailSignin'
export default {
  components: {
    EmailSignin
  }
}
</script>
emailSignin.vue
<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の概念を理解しきっていないので、

説明に使用してる言葉があやしいところもあるかもしれませんが、

無事解決できました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?