Saturday, February 27, 2021
Lưu Xuân Trường's Blog
  • Trang chủ
  • Backend
    • Laravel
    • Golang
    • NodeJS
    • MongoDB
    • Redis
    • WordPress
      • Kinh nghiệm
      • Plugins
  • Frontend
    • CSS
    • Javascript
      • ReactJS
      • UmiJS
      • VueJS
      • NuxtJS
      • Angular
      • RxJS
    • Game HTML5
  • Mobile
    • React Native
    • IOS
    • Android
    • Flutter
  • Tutorials
    • Redux-Saga
  • How to
  • Góc Ngoài Lề
    • Sống Chậm Nghĩ Sâu
    • Câu Hỏi Phỏng Vấn
    • IQ Test
  • Liên Hệ
No Result
View All Result
  • Trang chủ
  • Backend
    • Laravel
    • Golang
    • NodeJS
    • MongoDB
    • Redis
    • WordPress
      • Kinh nghiệm
      • Plugins
  • Frontend
    • CSS
    • Javascript
      • ReactJS
      • UmiJS
      • VueJS
      • NuxtJS
      • Angular
      • RxJS
    • Game HTML5
  • Mobile
    • React Native
    • IOS
    • Android
    • Flutter
  • Tutorials
    • Redux-Saga
  • How to
  • Góc Ngoài Lề
    • Sống Chậm Nghĩ Sâu
    • Câu Hỏi Phỏng Vấn
    • IQ Test
  • Liên Hệ
No Result
View All Result
Lưu Xuân Trường's Blog
No Result
View All Result

Quản lý trạng thái tải của side effect trong NuxtJS

truongluu by truongluu
28/01/2020
in NuxtJS, VueJS
Reading Time: 9min read
0 0
1
Quản lý trạng thái tải của side effect trong NuxtJS
0
SHARES
74
VIEWS
Share on FacebookShare on Twitter

Khái niệm side effect nghĩa là nó nói tới một xử lý hành động về bất đồng bộ (async) nào đó, ứng với một số framework thì nó có các khái niệm:

  1. ReactJS: Khi sử dụng redux và redux-thunk trong ứng dụng thì side effect mình sử dụng bên trong action
  2.  redux-saga thì side effect mình sử dụng trong các generator function
  3. ReactJS: Với kiến trúc dvaJS thì nó khai báo bên trong thuộc tính effects của model
  4. VueJS: Còn khi mình dùng nuxtJS thì nó được thực thi bên trong các actions tương ứng của store
  5. Angular: nếu bạn dùng package @ngrx/store thì nó lấy cảm hứng từ redux bên reactJS nên side effect nó cũng nằm ở actions. Trong trường hợp bạn không dùng @ngrx/store thì đa số là request api được viết trực tiếp bên trong service của angular

Điểm khác biệt giữa reducer và effects bên reactJS (actions và mutations bên vuex) đó là có sử dụng side effect hay không (có xảy ra hành động bất đồng bộ nào hay không)

khi làm việc với kiến trúc dvaJS chúng ta có khái niệm effects trong model, cái mà mình nói theo ý của mình đó là những thao tác liên quan tới side effect, thì bên này nó có một cái hay là cung cấp cho chúng ta thêm một plugin là loading để theo dõi được các effect được gọi tới, và đã thực hiện effect xong chưa. Ví dụ: khi gọi tới effect là login của model user, chúng ta hoàn toàn có thể quan sát được trạng thái đang gọi effect và hiện loading đang xử lý, và sau khi effect hoàn thành thì tắt loading đi.

Để quản lý state trong nuxtJS, chúng ta dùng vuex, vuex là gì cách dùng sao các bạn tham khảo vuex, bên nuxtJS thì việc xử lý các side effect nó nằm ở actions

Chúng ta xét thử một cấu trúc state đơn giản sau: auth.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import Cookies from 'js-cookie'
 
export const state = () => {
  return {
    auth: null,
    token: null
  }
}
 
export const mutations = {
  SET_AUTH(state, auth) {
    state.auth = auth
    Cookies.set('auth', auth)
  },
  SET_TOKEN(state, token) {
    state.token = token
    Cookies.set('token', token)
  },
  RESET_AUTH(state) {
    state.token = null
    state.auth = null
    Cookies.remove('token')
    Cookies.remove('auth')
  }
}
 
export const actions = {
  reset({ commit }) {
    commit('RESET_AUTH')
  },
  async login({ commit }, params) {
    const response = await this.$authApi.post('/auth/login', params)
    if (response && response.data) {
      const {
        data: { profile, token }
      } = response
      if (token) {
        commit('SET_TOKEN', token)
        commit('SET_AUTH', profile)
        this.app.router.push('/')
      }
    }
    return response
  },
 
  logout({ commit }) {
    commit('RESET_AUTH')
    this.app.router.push('/')
  }
}

Cấu trúc trên mình có khai báo state, mutations và actions. Việc tương tác từ component vue thì chúng ta thực hiện store.dispatch từ component. Khi bấm vào nút login chúng ta gọi:

1
store.dispatch('auth/login', {email, password})

dispatch tới action login của state auth và gởi theo tham số email, password

Vậy tình huống đưa ra là làm sao chúng ta quản lý được, ứng với side effect nào thì tiến trình của nó đang ở đâu, bắt đầu hay xong chưa, để hiện loading tương ứng, thông thường thì các bạn sẽ tạo ứng tới mổi state sẽ có thuộc tính là loading:

1
2
3
4
5
6
7
export const state = () => {
  return {
    auth: null,
    token: null,
    loading: false
  }
}

và khi gọi tới action nào thì chúng ta gọi thêm một mutation tương ứng là cập nhật loading = true | false, cấu trúc của state auth sẽ được thay đổi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import Cookies from 'js-cookie'
 
export const state = () => {
  return {
    auth: null,
    token: null,
    loading: false
  }
}
 
export const mutations = {
  SET_AUTH(state, auth) {
    state.auth = auth
    Cookies.set('auth', auth)
  },
  SET_TOKEN(state, token) {
    state.token = token
    Cookies.set('token', token)
  },
  RESET_AUTH(state) {
    state.token = null
    state.auth = null
    Cookies.remove('token')
    Cookies.remove('auth')
  },
  SET_LOADING(state, edit) {
    state.loading = edit
  }
}
 
export const getters = {
  isLoggedIn(state) {
    return state.auth !== null
  },
  token(state) {
    return state.token
  },
  auth(state) {
    return state.auth
  }
}
 
export const actions = {
  reset({ commit }) {
    commit('RESET_AUTH')
  },
  async login({ commit }, params) {
    commit('SET_LOADING', true)
    const response = await this.$authApi.post('/auth/login', params)
    commit('SET_LOADING', false)
    if (response && response.data) {
      const {
        data: { profile, token }
      } = response
      if (token) {
        commit('SET_TOKEN', token)
        commit('SET_AUTH', profile)
        this.app.router.push('/')
      }
    }
    return response
  },
 
  logout({ commit }) {
    commit('RESET_AUTH')
    this.app.router.push('/')
  }
}

Trước và sau khi thực hiện một side effect ta cập nhật lại trạng thái của  loading = true | false. và bên ngoài component ta theo dõi loading của từng state tương ứng và xử lý thao tác tương ứng, cách này chạy được, nhưng không hay, vì nó khá thủ công, bạn phải đi tạo loading ở tất cả các state và sẽ có những đoạn code lặp lại

Nếu bên reactJS với kiến trúc dvaJS, cung cấp cho ta một global model là dva-loading để quản lý được trạng thái của tất cả effects của các model khác trong ứng dụng reactJS, thì bên nuxtJS chúng ta làm bằng cách nào. Đó là chúng ta sử dụng một package vue-wait (vue plugin for global loading management), hiện tại mình đang thấy có 2 phiên bản cho vueJS và reactJS (nếu bạn dùng reactJS mà ko dùng kiến trúc dvaJS thì có thể áp dụng package này để quản lý trạng thái tải của ứng dụng). Thư viện này hổ trợ khá nhiều cho chúng ta là việc với reactJS, vueJS hoặc code javascript thuần cũng được :), quá tiện

Đầu tiên là chúng ta cài đặt package vue-wait:

1
yarn add vue-wait

vue-wait cho chúng ta cấu hình khi dùng chung với nuxtJS, bạn mã trên vào nuxt.config.js

1
2
3
4
5
6
7
8
9
10
11
12
{
  modules: [
    // Simple usage
    'vue-wait/nuxt'
 
    // Optionally passing options in module configuration
    ['vue-wait/nuxt', { useVuex: true }]
  ],
 
  // Optionally passing options in module top level configuration
  wait: { useVuex: true }
}

vue-wait cung cấp cho chúng ta nhiều cách thức để quản lý được tải của state. Ở đây, mình chỉ quan tâm tới

  1. global template helpers là
    1
    .is(loaders Array<String | Matcher>) or .waiting(loaders Array<String | Matcher>)

    helper này trả về kiểu là boolean nếu loaders đã cho tồn tại trong trang, giúp chúng ta kiểm tra được là action nào đang được tải trong trang (nghĩa là is(‘auth/login’) trả về true khi ta gọi dispatch(‘wait/start’, ‘auth/login’, { root: true}), ngược lại khi không tồn tại action auth/login hoặc gọi dispatch(‘wait/end’, ‘auth/login’, { root: true}) thì sẽ nhận được giá trị là false)
  2. action kèm theo để quản lý được trạng thái bắt đầu và kết thúc của 1 action
    1
    2
    dispatch('wait/start', actionName, { root: true });
    dispatch('wait/end', actionName, { root: true });

    Với 2 action wait/start và wait/end giúp chúng ta  thiết lập được trạng thái bắt đầu và kết thúc của 1 action. Việc này cũng giống như khi ta thiết lập lập trạng thái loading = true | false của từng model tương ứng

    Để xem thêm nhiều chức năng mà vue-wait cung cấp, các bạn tham khảo thêm ở trang chủ vue-wait

Vậy khi áp dụng vue-wait, thì đoạn mã auth.js được viết lại như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export const actions = {
  reset({ commit }) {
    commit('RESET_AUTH')
  },
  async login({ commit, dispatch }, params) {
    dispatch('wait/start', 'auth/login', true)
    const response = await this.$authApi.post('/auth/login', params)
    dispatch('wait/end', 'auth/login', false)
    if (response && response.data) {
      const {
        data: { profile, token }
      } = response
      if (token) {
        commit('SET_TOKEN', token)
        commit('SET_AUTH', profile)
        this.app.router.push('/')
      }
    }
    return response
  },
 
  logout({ commit }) {
    commit('RESET_AUTH')
    this.app.router.push('/')
  }
}

và bên page login ta thiết lập tải bằng theo dõi

1
2
3
<v-btn :loading="$wait.is('auth/login')" depressed @click="submit">
  Đăng NHẬP
</v-btn>

Khi ta gọi tới action với name là auth/login thì sẽ hiện tại đang tải ở nút “Đăng nhập”

Khi sử dụng thì ở view đã ổn thì ta có thể theo dõi được trạng thái tải của bất kỳ model nào rồi, còn bên trong action của mổi model thì thiết lập mã vẫn còn thủ công. Dựa vào những action mà vue-wait cung cấp, mình có cập nhật lại một tí là build một hàm chức năng toàn cục để có thể gọi trong component hoặc action

  1. Tạo một hàm chức năng callWithWaiter với đầu vào là actionName và payload
    1
    2
    3
    4
    5
    6
    export const callWithWaiter = dispatch => async (actionName, payload) => {
      dispatch('wait/start', actionName, { root: true })
      const response = await dispatch(actionName, payload)
      dispatch('wait/end', actionName, { root: true })
      return (response && response.data) || null
    }

    khi gọi hàm thì nó cập nhật lại trạng thái tải của action tương ứng và trả về giá trị cần thiết
  2. Thiết lập hàm toàn cục để có thể gọi ở trong component hoặc actions, bằng cách sử dụng plugin trong nuxtJS
    1
    2
    3
    4
    5
    6
    7
    import rules from '@/configs/rules'
    import { callWithWaiter } from '@/helpers/ultils'
     
    export default ({ app: { store } }, inject) => {
      inject('actionWatcher', callWithWaiter(store.dispatch))
      inject('rules', rules)
    }

    Đăng ký global plugin vào trong nuxt.config.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    plugins: [
        {
          src: '~plugins/vue-carousel',
          ssr: false
        },
        {
          src: '~plugins/global'
        },
        {
          src: '~plugins/axios',
          ssr: false
        },
        {
          src: '~plugins/helpers'
        },
        {
          src: '~plugins/filters'
        }
      ],

    và cập nhật lại mã trong trang đăng nhập của ứng dụng, bằng cách gọi tới hàm toàn cục $actionWatcher
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <script>
    export default {
      middleware: 'notAuthenticated',
      layout: 'default',
      components: {},
      data: () => ({
        email: '',
        password: ''
      }),
      methods: {
        submit(event) {
          if (this.$refs.form.validate()) {
            this.$actionWatcher('auth/login', {
              email: this.email,
              password: this.password
            })
          }
        }
      }
    }
    </script>

    Vậy với việc sử dụng package vue-wait và thêm một hàm chức năng nhỏ, chúng ta hoàn toàn quản lý được việc thay đổi trạng thái loading của ứng dụng nuxtJS
Tags: dvaJSnuxtJSreduxredux-sagaredux-thunkside effect
Previous Post

Giới thiệu về UmiJS P2 (Cấu trúc thư mục, cấu hình thường dùng)

Next Post

Phân biệt cách sử dụng fetch vs asyncData trong nuxtJS

truongluu

truongluu

Next Post
Phân biệt cách sử dụng fetch vs asyncData trong nuxtJS

Phân biệt cách sử dụng fetch vs asyncData trong nuxtJS

Comments 1

  1. Pingback: Theo dõi action trong NuxtJS - Lưu Xuân Trường's Blog

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You should learn from your competitor, but never copy. Copy and you die.

Jack Ma

Quotations

About me

Field of interests: Svelte, ReactJS, Angular, VueJS, React Native, Flutter, NodeJS, MongoDB, Message Broker, WordPress, AdonisJS, NestJS, NuxtJS, Docker, Microservice, Arduino, Game HTML5. Hope to share my experiences with everybody

Recent Posts

  • Code push in React Native
  • What’s middleware? How does it work?
  • Styled components trong ReactJS
  • Frontity cơ bản phần I (Tạo và chạy dự án)
  • React framework for WordPress

Recent Comments

  • truongluu on Giới thiệu về UmiJS P2 (Cấu trúc thư mục, cấu hình thường dùng)
  • Hung on Giới thiệu về UmiJS P2 (Cấu trúc thư mục, cấu hình thường dùng)
  • Redux Saga phần III (Tips khi làm việc) - Lưu Xuân Trường's Blog on Quản lý trạng thái tải khi dùng redux-saga
  • Redux Saga phần II (Các khái niệm cơ bản) - Lưu Xuân Trường's Blog on Cheatsheets Non Blocking, Blocking Redux Saga
  • truongluu on Giới thiệu về UmiJS P4 (Server Side Rendering)

Archives

  • February 2021
  • January 2021
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • March 2020
  • February 2020
  • January 2020
  • June 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • April 2018
  • March 2018

Categories

  • AdonisJS
  • Angular
  • Arduino
  • Backend
  • Câu Hỏi Phỏng Vấn
  • CSS
  • Ebook
  • Frontend
  • Frontity
  • Góc Ngoài Lề
  • How to
  • IOS
  • IQ Test
  • Javascript
  • Kinh nghiệm
  • Kinh nghiệm làm việc
  • Máy chủ
  • Mobile
  • MongoDB
  • NestJS
  • NodeJS
  • NuxtJS
  • Plugins
  • React Native
  • React Native
  • ReactJS
  • Redis
  • Redux-Saga
  • RxJS
  • Tutorials
  • UmiJS
  • Uncategorized
  • VueJS
  • WordPress

Friends

Phu's blog
  • RxJS toàn tập (P1 giới thiệu RxJS là gì)

    RxJS toàn tập (P1 giới thiệu RxJS là gì)

    0 shares
    Share 0 Tweet 0
  • Giới thiệu về UmiJS P1 (Tổng quan về umiJS)

    0 shares
    Share 0 Tweet 0
  • Giới thiệu về UmiJS P2 (Cấu trúc thư mục, cấu hình thường dùng)

    0 shares
    Share 0 Tweet 0
  • Redux Saga phần I (Giới thiệu)

    0 shares
    Share 0 Tweet 0
  • Một số câu hỏi phỏng vấn frontend developer

    0 shares
    Share 0 Tweet 0

About me

Lưu Xuân Trường

Field of interests: Svelte, ReactJS, Angular, VueJS, React Native, Flutter, NodeJS, MongoDB, Message Broker, WordPress, AdonisJS, NestJS, NuxtJS, Docker, Microservice, Arduino, Game HTML5. Hope to share my experiences with everybody

© 2020 https://luuxuantruong.info

No Result
View All Result
  • Trang chủ
  • Backend
    • Laravel
    • Golang
    • NodeJS
    • MongoDB
    • Redis
    • WordPress
      • Kinh nghiệm
      • Plugins
  • Frontend
    • CSS
    • Javascript
      • ReactJS
      • UmiJS
      • VueJS
      • NuxtJS
      • Angular
      • RxJS
    • Game HTML5
  • Mobile
    • React Native
    • IOS
    • Android
    • Flutter
  • Tutorials
    • Redux-Saga
  • How to
  • Góc Ngoài Lề
    • Sống Chậm Nghĩ Sâu
    • Câu Hỏi Phỏng Vấn
    • IQ Test
  • Liên Hệ

© 2020 https://luuxuantruong.info

Welcome Back!

Login to your account below

Forgotten Password?

Create New Account!

Fill the forms below to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In