0.5.1 โ€ข Published 3 years ago

@andrewbrennanfr/burrito v0.5.1

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

๐ŸŒฏ Burrito

Build a Vuex store that is strictly formatted, strongly typed & unit tested ๐Ÿฅณ

Release Version Downloads License Keywords

๐Ÿงถ Installation

NPM

npm install @andrewbrennanfr/burrito

Yarn

yarn add @andrewbrennanfr/burrito

Import

import Burrito, { Item, Module, Status } from "@andrewbrennanfr/burrito";

๐Ÿš€ Usage

Suppose we are building a comments feature, allowing users to write comments on a post.

First we must make a commentsModule to use in the Vuex store.

import Burrito from "@andrewbrennanfr/burrito";

export type Comment = {
    body: string;
    id: string;
    postId: string;
};

export type CommentFetchParams = {
    postId: string;
};

export const commentsModule = Burrito.module<Comment, CommentFetchParams>(
    {
        getDataKey: (comment) => comment.id,
        getParamsKey: (params) => params.postId,
    },
    {
        add: (comment) =>
            new Promise<Comment>((resolve, reject) => {
                /**
                 * Perform API request to add a comment
                 *
                 * If successful: resolve(Comment)
                 * Otherwise: reject(string)
                 */
            }),

        edit: (oldComment, newComment) =>
            new Promise<Comment>((resolve, reject) => {
                /**
                 * Perform API request to edit a comment
                 *
                 * If successful: resolve(Comment)
                 * Otherwise: reject(string)
                 */
            }),

        fetch: (params) =>
            new Promise<Comment[]>((resolve, reject) => {
                /**
                 * Perform API request to fetch comments
                 *
                 * If successful: resolve(Comment[])
                 * Otherwise: reject(string)
                 */
            }),

        refetch: (params) =>
            new Promise<Comment[]>((resolve, reject) => {
                /**
                 * Perform API request to refetch comments
                 *
                 * If successful: resolve(Comment[])
                 * Otherwise: reject(string)
                 */
            }),

        remove: (comment) =>
            new Promise<void>((resolve, reject) => {
                /**
                 * Perform API request to remove a comment
                 *
                 * If successful: resolve(void)
                 * Otherwise: reject(string)
                 */
            }),
    }
);

This module can be used like any other Vuex module.

import { commentsModule } from "/path/to/commentsModule";
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
    actions: {},
    modules: {
        comments: commentsModule,
    },
    mutations: {},
    state: {},
});

Next we will make this available to our Vue components.

import Burrito from "@andrewbrennanfr/burrito";
import Vue from "vue";

Vue.use(Burrito);

Components can now access the module's data by referencing this.$modules.comments.

The $modules plugin exposes the following information about comments:

type Module = {
    actions: {
        add: (data: Comment) => void;
        clear: (getClearCondition: (data: Comment) => boolean) => void;
        edit: (oldData: Comment, newData: Comment) => void;
        fetch: (params: CommentFetchParams) => void;
        refetch: (params: CommentFetchParams) => void;
        remove: (data: Comment) => void;
    };

    state: {
        items: {
            [commentId: string]:
                | {
                      data: Comment;
                      status: Error | "adding" | "editing" | "removing" | null;
                  }
                | undefined;
        };

        list: {
            data: Comment;
            status: Error | "adding" | "editing" | "removing" | null;
        }[];

        listGroupedBy: Partial<
            Record<
                string,
                {
                    data: Comment;
                    status: Error | "adding" | "editing" | "removing" | null;
                }[]
            >
        >;

        listSortedBy: {
            data: Comment;
            status: Error | "adding" | "editing" | "removing" | null;
        }[];

        status: {
            [postId: string]:
                | Error
                | "fetched"
                | "fetching"
                | "refetched"
                | "refetching"
                | undefined;
        };
    };
};

Lastly, we must add the type definition, so that typescript understands the structure of commentsModule.

import { Module } from "@andrewbrennanfr/burrito";

declare module "vue/types/vue" {
    interface Vue {
        $modules: {
            comments: Module<Comment, CommentsFetchParams>;
        };
    }
}

That's it! ๐Ÿฅณ Our commentsModule is available, with full type safety, in our Vue components.

Now the actions (add, edit, fetch, refetch, remove) automatically synchronize commentsModule with the API.

Consider the following example:

@Component
export default class App extends Vue {
    private get comment() {
        return this.$modules.comments.state.items[this.commentId];
    }

    private handleUpdateCommentText() {
        this.$modules.comments.actions.edit(this.comment, {
            ...this.comment,
            body: "Goodbye everyone!",
        });
    }
}

Initially, the value of this.comment would look something like:

{
    data: {
        body: "Hello world!",
        id: "123",
        postId: "456",
    },
    status: null,
};

By calling this.handleUpdateCommentText, our comment would be sent to the API & in the meantime would optimistically update to look like:

{
    data: {
-       body: "Hello world!",
+       body: "Goodbye everyone!",
        id: "123",
        postId: "456",
    },
-   status: null,
+   status: "editing",
};

The UI can be updated to reflect the new optimistic state of the comment & show the current editing status.

If the dispatched API request resolves successfully, the comment will revert back to status: null.

If the request is rejected, the values status will reflect this too.

The fetching/refetching status for a set of comments on a post, can be determined by accessing this.$modules.comments.state.status[postId].

๐Ÿ” Example

To try a live example, run the commands below & open localhost:8080 in your browser.

git clone git@github.com:andrewbrennanfr/burrito.git

cd burrito

yarn install

yarn serve

๐Ÿ“ฃ FAQ

What is the problem that you're trying to solve?

This library provides an opinionated design pattern, for storing application state & interacting with an API. It handles common behaviors like optimistic updating & error handling out-of-the-box, reducing boilerplate. The goal is to offer a type-safe way to interact with the the Vuex store, that is predictable & well tested.

What is the difference between fetch & refetch?

It's expected that both return arrays of data. However, fetch will merge the new items into the existing data in the store & refetch will remove all existing items, before applying the new items to the store.

Why did you call the library Burrito?

Who doesn't love burritos? ๐Ÿ˜ ๐ŸŒฏ

โ™ป๏ธ Versioning

This library aims to follow the SemVer (MAJOR.MINOR.PATCH) convention.

In short, this means:

  • MAJOR versions (v1.0.0 => v2.0.0) will be incremented for breaking changes
  • MINOR versions (v1.0.0 => v1.1.0) will be incremented for non-breaking changes
  • PATCH versions (v1.0.0 => v1.0.1) will be incremented for tweaks, bug fixes & documentation changes

โš ๏ธ Please note, before v1.0.0 all MINOR & PATCH increments should be considered breaking!

๐Ÿ‘€ License

MIT

Copyright (c) 2021 Andrew Brennan

0.5.0

3 years ago

0.4.1

3 years ago

0.4.0

3 years ago

0.5.1

3 years ago

0.3.0

3 years ago

0.2.2

3 years ago

0.2.1

3 years ago

0.2.0

3 years ago

0.1.0

3 years ago