LoginSignup
11
5

More than 1 year has passed since last update.

Vuexの状態管理 is not 情報の永続保存ではないということ

Last updated at Posted at 2021-05-26

Vuexとは?

Vuexの概念を理解するのがむずかしい・・・
Vuexは状態管理のためのライブラリだと言われている。
よく木に例えられたりするが、自分には合わない説明だったので自分の言葉でまとめたいと思う。

利便性

なぜ、Vuexがあると良いのか、むしろないと困ってしまうのか?ということについて考えてみよう。
例えば、親子関係が複雑なVueプロジェクトがあったとする。この時、あるデータを親から子のコンポーネントへ移す時は propsを使うであろう。しかし、この場合、その構造が複雑になればなるほどほど受け渡しの記述が多くなったり、変更があった時に大変であろう。
props.png
もし、この時一貫した情報やメソッドを保持したりする中心的な場所があれば、上記のような構造での記述や受け渡しでのミスを減らすことができる。これがVuexであり、便利なところである。
この store があれば各コンポーネントで取得したい情報や利用したいメソッドを同時に保持することができて、同じ取り出し方で利用することができる。
props.png

各プロパティ

この部分が一番理解するのに困る点であろう。なぜなら、各プロパティの機能に加えてそのプロパティを利用するために関連性を持つメソッドも出てきて情報量が多すぎるからだ。まずは細分化して考えて見ていただきたい。

state

これはもうdataプロパティと同じで捉えて良いと考えている。様々なコンポーネントやページで使い共有される情報をプロパティとして state に定義する。

index.js
export default new Vuex.Store({
  state: {
    name:"",
    email:"",
    password:""
  },

getter

state の情報を得るものとして使われる。自分の感覚では算出プロパティに近い扱いである。storegetter を定義すると state の状態変化に基づいてその値を変化させて返す。ここで注意したいのは state の値自体を変化させるものではなく、 state の値をもとに getter で変化させるということだ。値の変更は mutation で行う。

index.js
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

mutation

mutation は唯一state の値を変更させる方法と公式には言われているが、自分の中ではstateを更新するセッターと捉えている。第一引数に元の状態である state を持って第二引数に更新情報となる payload を用いてその結果でstateの値を更新させる。また、このmutation を呼び出すときに使うのが commit である。

index.js
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 状態を変更する
      state.count++
    }
  }
})
// ...コミットで呼び出す
store.commit('increment', 10)

action

actionmutation の呼び出し役かつ関数処理の役割があると考えている。非同期処理をかけるのが mutation との違いであり、処理はactionmutation でその変更を state に保存するという役割の分け方が公式の意向にも沿っており良いと思われる。
またコンポーネント側からは dispatch を用いて呼び出す。

index.js
new Vuex.Store({
  state: {
      amount: 0
  },
  mutations: {
      tax (state, totalPrice) {
          state.amount = totalPrice % 10;
      },
  },
  actions: {
      calPrice (context,num) {
          price = axios.get(リクエストURL);
          totalPrice = price * num;
          context.commit('tax', totalPrice);
      },
  },
}
// ...ディスパッチで呼ぶ
$store.dispatch('calPrice');

新規登録とログイン処理

このVuexの処理の流れを掴むために新規登録とログイン処理を簡易的に作ってみた。
処理の流れとしては新規登録をすると、myPage.vueでユーザー情報を確認できる。ログインをして成功するとmuPage.vueでユーザー情報を確認できる。失敗すると、myPage.vueには遷移できないというものだ。

App.vue -----register.vue ----- myPage.vue
       \                      /
        -----login.vue -------
App.vue
<template>
<div id="app">
  <div class="flex">
    <p @click="$router.push('/register')">新規登録</p>
    <p @click="$router.push('/login')">ログイン</p>
  </div>
  <router-view></router-view>
</div>
</template>

<style>
  .flex {
    display: flex;
    justify-content: space-between;
    margin: 0 auto;
    width: 50%;
  }
  .card {
    margin: 100px auto;
    width: 350px;

    border-radius: 5px;
    padding: 20px;
  }
  .form {
    display: flex;
    flex-direction: column;
  }
  button {
    margin: 0 auto;
    width: 50%;
  }
</style>
pages/register.vue
<template>
  <div>
    <div class="card">
      <div class="form">
        <input placeholder="ユーザーネーム" type="text" v-model="name" />
        <input placeholder="メールアドレス" type="email" v-model="email" />
        <input placeholder="パスワード" type="password" v-model="password" />
        <button @click="register">新規登録</button>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: "",
      email: "",
      password: "",
      userInfo:[]
    }
  },
  methods: {
    register() {
      const userInfo = {
        name: this.name,
        email: this.email,
        password: this.password
      }
      this.$store.dispatch("register",userInfo).then(() => {
          this.$router.push({path:'myPage'})
        }
      );
    }
  }
}
</script>

pages/Login.vue
<template>
  <div>
    <div class="card">
      <div class="form">
        <input placeholder="メールアドレス" type="email" v-model="email" />
        <input placeholder="パスワード" type="password" v-model="password" />
        <button @click="auth">ログイン</button>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      email: "",
      password: "",
      loginInfo:[]
    }
  },
  methods: {
    auth() {
      const loginInfo = {
        email: this.email,
        password: this.password
      }
      this.$store.dispatch("login",loginInfo).then(() => {
        this.$router.push({path:'myPage'})
      });
    }
  }
}
</script>

myPage.vue
<template>
  <div id="app">
    <p>{{$store.state.name}}</p>
    <p>{{$store.state.email}}</p>
    <p>{{$store.state.password}}</p>
  </div>
</template>

store 内にはユーザー情報のためにname email passwprd といったstate を用意している。 mutation には値の更新のための signUp auth メソッド、action にはそれらを呼び出すための register ,login メソッドが定義されている。

store/index.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    name:"",
    email:"",
    password:""
  },
  mutations: {
    signUp(state, payload) {
      state.name = payload.name;
      state.email = payload.email;
      state.password = payload.password;
    },
    auth(state,payload) {
      state.email = payload.email;
      state.password = payload.password;
    }

  },
  actions: {
      register({ commit }, userInfo ){
        commit("signUp", userInfo);

      },
      login({ commit, state },  loginInfo ) {
        if( state.password === loginInfo.password) {
          commit("auth",loginInfo );
          console.log("成功");
        } else {
          this.router.push("/");
        }
    }
  },
});

ここでログイン成功時にはどんな処理が行われているかまとめてみる
ライフ.png
まず、ボタンのクリックでloginメソッドがloginアクションを呼び、そのactionが入力したパスワードとメアドがstateの各値と一致したらauth ミュテーションを呼んで更新するようにしている。
新規登録はこの中でloginアクションのstate.passwordstate.emailとの整合性確認がないだけである。

これを実際のコードで順番に合わせるとこのようになる。
MacBook Pro - 1.png

一般化

さらに、これを一般化させる。今回はgetterdiaptchcommitも含めて分析してみる。

ライフ2.png
やっていることは同じである。
①イベント発火でメソッドが実行させる。
②メソッドがdispatchを通してaction を呼ぶ。
actioncommitを通してmutationを呼ぶ。
mutationstateを更新させる。
stateに値が保存される。
computed を通してgetterが値の変更を感知して値を変化させる。


だが、これには大きな欠点がある。それはstate がリロードすると値を消失してしまうことである。
具体的には、リロードするとstateから新規登録した値がなくなってしまうため、ログインできなくなってしまうということである。
それを補う役割をするのが localStorage である。

localStorage

localStorage は値をブラウザ上にキーとバリューセットで保存するものである。
以下localStorageに保存、取得、削除させるメソッドである。

localStorage.setItem('キー', '');//保存
localStorage.getItem('キー');//取得
localStorage.removeItem('キー');//削除

また、localStorageは文字列形式で値が格納されるので、保存前にオブジェクトをJSON形式に変換しなければならない。
逆に、取得したときの値はJSON形式からオブジェクトに戻さなければならない。

JSON.stringify(obj);//オブジェクト→JSON
JSON.parse(getjson);//JSON→オブジェクト

Vuexでの利用

ここでリロードにより値が保存されないのならば、値が保存されるようにlocalStorageを利用しようという考え方である。
以下、localStorage を加えてブラウザ上に値を保存するようにしたバージョンのstoreである。

store/index.js
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    name:"",
    email:"",
    password:""
  },
  mutations: {
    signUp(state, payload) {
      state.name = payload.name;
      state.email = payload.email;
      state.password = payload.password;
    },
    auth(state,payload) {
      state.email = payload.email;
      state.password = payload.password;
    }

  },
  actions: {
      register({ commit }, userInfo ){
        var setUser = JSON.stringify(userInfo);
        localStorage.setItem('setUser', setUser);
        commit("signUp", userInfo);

      },
      login({ commit, state },  loginInfo ) {
        var jsonObj = localStorage.getItem('setUser');
        var jsPar = JSON.parse(jsonObj);
        if(state.password === jsPar.password && state.email === jsPar.email) {
          console.log("成功");
        }else {
          this.router.push("/");
        }
    }
  },
});

actionsloginregisterメソッドで上記のメソッドを用いている。
register ではlocalStorageに保存し、loginではlocalStorageからキーで保存したオブジェクトを取り出してその値とstateの値の整合性を確認している。
処理としては以前のindex.jsと同じである。

まとめ

前提であるVuexの説明がメインとなってしまったが、リロードでVuexが情報を保存するのではなく状態管理のためのライブラリであるということが理解できたであろう。

そのため、実践的にはリロードすることを前提としてlocalStorageなどに保存することが多いだろう。また、Vue.jsではわざわざこのlocalStorage を利用せずともvuex-persistedstateというライブラリで自動的に保存する機能がある。
また次の機会にこのライブラリを用いてVuexを用いた情報の永続保存について実践していきたいと思う。

参考

https://vuex.vuejs.org/ja/
https://youtu.be/9Lht5mZ9zxw
https://youtu.be/tf6HWsyAVv4

11
5
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
11
5