Managing state with Pinia and Nuxt 3 - Using a simple add to cart instance
Tags: pinia, nuxt, state, webdev
Published: 2023-06-13 12:51:57


## Introduction
*One of the crucial aspects of frontend development is state management.*

State management allows developers to maintain and manage the state of their applications. State here refers to the information that your web application needs to recall to keep the system working smoothly. 

Some reasons why state management is important in web development:

* Improving the performance of a web application by reducing the number of unnecessary re-renders and improves the speed and efficiency of the application.
* Ensuring the consistency of data across different components of the application hereby  preventing errors and improving the user experience.
* Ensuring that the application remains scalable and maintainable over time and also making it easier to debug an application by providing a clear picture of the state of the application at any given time.

In this article, we will be focusing on Pinia as a state management library and how it is used in Nuxt applications.

## Pinia and Nuxt 3
### PINIA ~ The State Manager
Pinia is a state management library for Vue.js applications that provides a centralized store for easy access to data from any component/page in the application using a more reactive approach. 


### Starting up your Nuxt 3 project
On the terminal,  paste the code below to install Nuxt 3 and start up your project:

`npx nuxi init <project-name>`

The following prompts will pop up in order to proceed. Follow the instructions.



* cd into the folder using `cd <project-name>`
* Finally, you can install the dependencies using either yarn install or npm install. But for this tutorial, we will be working with yarn.
* And Voila!!!, Your Nuxt 3 Project is ready
    
### Setting up Pinia
* Install Pinia to your Nuxt app using:
`yarn add @pinia/nuxt` or `npm install @pinia/nuxt`
* In your project folder, navigate to your nuxt.config.ts file and add Pinia to your modules in this format;

```javascript!
// nuxt.config.ts
 
modules: ["@pinia/nuxt"],
```
- Navigate to the store folder and create a file which for the sake of this tutorial, will be named, cart.js.

## Use Case
### Objective: 
A simple application that imitates an ecommerce platform for shopping. We want to be able to add to cart and modify our cart from the store using pinia as the state manager.

### Creating the Cart State
Firstly, we have to import the already installed pinia packages to be able to use its storage. Navigate to the *cart.js* file.

The "*defineStore*" function takes two arguments: the name of the store and an object containing the store's state and actions. In this case, the name of the store is "cart". The object contains a "state" property, which is a function that returns an object containing the initial state of the store. In this case, the initial state is an empty array assigned to a property named "*cart*".

Next we export the cart store as a constant in a way that is easy to understand, it is advisable to use a name that is related to the store. In this example, we can use the name "*useCartStore*" to export the cart store. The name "*cart*" represents the file name, which is "*cart.js*". By exporting the cart store as a constant, we can easily access it in other parts of our code and perform actions on the cart state. 	



``` javascript
// cart.js


import { defineStore } from "pinia";
 
export const useCartStore = defineStore('cart', {
  state: () => {
    return {
      cart: [],
    };
  },
})
```

In the code above, We want to store our cart and also be able to modify it. So create a  cart state and make it an empty array.

### Adding to Cart
Setting up the Nuxt Component/Page Structure: Create a component called products.vue


``` javascript
// src/components/Products.vue


<template>
  <div>
    <div v-for="(course, index) in courses" :key="index">
       <span>{{ item.title }} - {{ item.price }}</span>
       <button>Add to cart</button>    
    </div>
  </div>
</template>


<script setup>
const courses = ref([
  {
    id: "itm1",
    price: "65.00",
    quantity: 0,
    title: "Animation",
  },
  {
    id: "itm2",
    price: "52.00",
    quantity: 0,
    title: "Design",
  },
  {
    id: "itm3",
    price: "76.00",
    quantity: 0,
    title: "Photography",
  },
  {
    id: "itm4",
    price: "84.00",
    quantity: 0,
    title: "Crypto",
  },
]);
</script>
```

### Click to add To Cart: 
To enhance the functionality of the web application, you need to add a function to the "Add to Cart" button on the "Products.vue" file. This function should accept the item that the user intends to add to the cart as a parameter. You should already be familiar with the "v-for" directive, which is used to loop through items and display them in the HTML template. If you are not familiar with this directive, please refer to the appropriate documentation or tutorial. 

* The @click event on the button is set to a function that is called whenever the button is clicked. This function calls the addToCart method and passes in the item object as a parameter. The addToCart method is responsible for adding the item to the shopping cart.The button's text changes depending on whether the item is already in the shopping cart. This is determined by the `alreadyInCart` method, which returns true if the item is already in the cart, and false if it is not.

``` javascript
// src/components/products.vue


<template>
  <div>
    <div v-for="(course, index) in courses" :key="index">
          <span>{{ item.title }} - {{ item.price }}</span>
          <button
            @click="() => {addToCart(item)}"
            >{{ alreadyInCart(item) ? 'In Cart' : 'Add to cart' }}</button
          >
    </div>
  </div>
</template>
```


* Now, we navigate to the cart.js file and create an action called addToCart() This action takes an item as a parameter and adds a new object to the "cart" array. This object is created by copying the original "item" using the spread syntax (...) and adding a "quantity" property with a value of 1. This will keep track of the quantity of the item in the cart.


``` javascript
 // store/cart.js


import { defineStore } from "pinia";


export const useCartStore = defineStore('cart', {
 state: () => {
    return {
      cart: [],
    };
  },


  actions: {
    addToCart(item) {
      this.cart.push({ ...item, quantity: 1 })
    },
  },
})

```


* In the below code, we access the store action from the product.vue component in order to actually pass a new product to the store and add it to cart state.The useCartStore function returns a reactive instance of the cart store defined in the "cart.js" file. The instance is assigned to a variable called "cartStore".Two functions are defined within the script setup block.The first function, "alreadyInCart", accepts an item as a parameter and checks if the item is already in the cart by using the "find" method on the "cartStore.cart" array.
The second function, "addToCart", accepts an item as a parameter and checks if the item is already in the cart. If the item is not already in the cart, it is added by calling the "addToCart" action on the "cartStore". If the item is already in the cart, an alert is displayed. 


``` javascript
 // src/components/products.vue

<script setup>
import { useCartStore } from "~~/store/cart";

const cartStore = useCartStore();

const alreadyInCart = (item) => {
  const x = cartStore.cart?.find(el => el.id === item.id)
  if(x?.id) {
    return true
  } else {
    return false
  }
}

const addToCart = (item) => {
  if(!alreadyInCart(item)) {
    cartStore.addToCart(item);
  } else {
    alert(`${item.title} already in cart`)
  }
};
</script>
```


## Conclusion
Managing state with Pinia is an effective way to handle the state of a Vue.js application. With Pinia and Nuxt 3, developers can create dynamic and interactive features such as a shopping cart. The example provided shows how easy it is to use Pinia to manage state and how it can be used to build a simple add-to-cart feature. Overall, Pinia and Nuxt 3 make a powerful combination for building scalable and maintainable Vue.js applications.