Giter Site home page Giter Site logo

andy-set-studio / beedle Goto Github PK

View Code? Open in Web Editor NEW
385.0 3.0 40.0 2.51 MB

A tiny library inspired by Redux & Vuex to help you manage state in your JavaScript apps

Home Page: https://beedle.hankchizljaw.io

License: MIT License

JavaScript 99.79% HTML 0.01% Vue 0.20%
state-management state-machine vanilla-javascript lightweight minimal reactive

beedle's Introduction

Beedle

The current build status based on whether tests are passing The Uncompressed size of Beedle The GZIP size of Beedle The Brotli size of Beedle License: MIT

Beedle is a tiny library to help you manage state across your application. Inspired by great libraries like Vuex and Redux, Beedle creates a central store that enables you predictably control and cascade state across your application.

This library was initially created as a prototype for this article on CSS-Tricks, where you learn how to build a state management system from scratch with Vanilla JavaScript.

See the documentation โ€” See the project structure

Demos

How it works

Beedle creates a pattern where a single source of truth, the 'Application State' cascades state across your app in a predictable fashion. To modify state, a set flow of actions and mutations help create a traceable data-flow that makes things a little easier to debug.

Using a Pub/Sub pattern which notifies anything that is subscribed to changes, a fully reactive front-end can be achieved with a few kilobytes of vanilla JavaScript.

A flow diagram that shows an action that calls a mutation, which mutates the state and triggers an update to anything that is listening

As the diagram above shows, a simple, predictable flow is created by pushing data into an action which subsequently calls one or more mutations. Only the mutation can modify state, which helps with keeping track of changes.

Continue reading the documentation

A mini library for small projects

Beedle is inspired by libraries like Redux, but certainly isn't designed to replace it. Beedle is aimed more at tiny little applications or where a development team might be looking to create the smallest possible footprint with their JavaScript.

Performance budget

Beedle is intended to be tiny, so the largest that the uncompressed size will ever get to is 5kb.

Browser support

Beedle is aimed at browsers that support ES6 by default. It also uses a Proxy to monitor state, so anything that supports Proxy will support Beedle.

You could use the Proxy polyfill to support more browsers.

Most major browsers will support Beedle with no issues.

Getting started

You can pull Beedle down via npm or take a zip of this repository. The rest of this guide assumes you've used npm.

1) Install

Run npm install beedle in your project directory.

2) Create a store instance

First up, import it into your JavaScript:

import Store from 'beedle';

Once you've got that you should create some actions, mutations and some initial state:

const actions = {
    saySomething(context, payload) {
        context.commit('setMessage', payload);
    }
};

const mutations = {
    setMessage(state, payload) {
        state.message = payload;

        return state;
    }
};

const initialState = {
    message: 'Hello, world'
};

Once you've got those setup, you can create a Store instance like this:

const storeInstance = new Store({
    actions,
    mutations,
    initialState
});

3) Use in your app

Let's say you've got a text box that you type a message into. When the content is changed, it could dispatch a new message to your store:

// Grab the textarea and dispatch the action on 'input'
const textElement = document.querySelector('textarea');

textElement.addEventListener('input', () => {

    // Dispatch the action, which will subsequently pass this message to the mutation
    // which in turn, updates the store's state
    storeInstance.dispatch('saySomething', textElement.value.trim());
});

4) Listen for changes

Beedle uses the Pub/Sub pattern to transmit changes. Let's attach the message to a DOM element:

// Grab the text element and attach it to the stateChange event
const messageElement = document.querySelector('.js-message-element');

// This fires every time the state updates
storeInstance.subscribe(state => {
    messageElement.innerText = state.message;
});

Head over to the basic demo to see this in action ๐Ÿš€

Acknowledgements

Thanks to Eli Fitch for giving me the idea to call this Beedle. This matches my preference to call my little projects names based on Zelda. Here's Beedle from Zelda.

Thanks to the incredible people who maintain projects such as Redux, Vuex and MobX et. al. Thanks for making our lives easier and for inspiring this project.

beedle's People

Contributors

0xflotus avatar andy-set-studio avatar ppeeou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

beedle's Issues

Store is not defined

Expected Behavior

Loading beedle from unpkg.com CDN directly on browser

Actual Behavior

This error is reported: Uncaught ReferenceError: Store is not defined

Steps to Reproduce the Problem

<script src="//unpkg.com/beedle"></script>
<textarea></textarea> <script> const actions = { saySomething(context, payload) { context.commit('setMessage', payload); } }; const mutations = { setMessage(state, payload) { state.message = payload; return state; } }; const initialState = { message: 'Hello, world' }; const storeInstance = new Store({ actions, mutations, initialState }); const textElement = document.querySelector('textarea'); textElement.addEventListener('input', () => { // Dispatch the action, which will subsequently pass this message to the mutation // which in turn, updates the store's state storeInstance.dispatch('saySomething', textElement.value.trim()); }); // Grab the text element and attach it to the stateChange event const messageElement = document.querySelector('.js-message-element'); // This fires every time the state updates storeInstance.subscribe(state => { messageElement.innerText = state.message; }); </script>

My Browser link(s)

stateChange event fires once for each property in the state due to incorrect object.assign use

I implemented this code as written in
https://css-tricks.com/build-a-state-management-system-with-vanilla-javascript/

I wanted to comment there, but the thread is closed.

Issue

I have say 12 properties in my state, and with each mutation, the whole state object gets replaced, thus the 'stateChange' event is getting fired 12 times.

Expected Behavior

Expected console.log output--

store.js:52 ACTION: changePage
store.js:27 stateChange: currentPage: 1

Actual Behavior

Actual console.log output--
ACTION: setLoadingState
store.js:52 ACTION: changePage
store.js:27 stateChange: currentPage: 1
store.js:27 stateChange: currentPage: 1
store.js:27 stateChange: lastPage: 0
store.js:27 stateChange: direction: down
store.js:27 stateChange: isLoading: false
store.js:27 stateChange: isGated: false
store.js:27 stateChange: isChallenge: false
store.js:27 stateChange: isLockedState: true
store.js:27 stateChange: nickname:
store.js:27 stateChange: showWave: false
store.js:27 stateChange: challengePoints: 0
store.js:27 stateChange: challengePages: 3,5,7,10

Steps to Reproduce the Problem

  1. Add multiple properties to the state
  2. Subscribe to the 'stateChange' event
  3. Use the app in the browser and view console.log output

I can make a PR if we decide this change would be helpful.

Current Implementation
self.state = Object.assign(self.state, newState);

Solution: Start with an empty object for mutating the state.
self.state = Object.assign({}, self.state, newState);
this.setTrap(); // then call a method to re-init the set trap Proxy

modularize the Stor

Expected Behavior

Could you please give us an idea, how to modularize this store

Add a persistent state option

It could be handy for state to persist in local storage. This is especially true for small, PWAs.

Todos

  • Add constructor parameter
  • Feature detect that storage is available
  • Hydrate from local storage on instantiation if enabled
  • Self-subscribe to changes and automatically update storage
  • Add clear method if the local storage needs clearing for some reason

Refusing to install beedle as a dependency of itself

Expected Behavior

npm install beedle => installation OK

Actual Behavior

npm install beedle

npm ERR! Linux 4.4.30-32.54.amzn1.x86_64
npm ERR! argv "/z_local/nodeJS/node-v6.9.1-linux-x64/bin/node" "/z_local/nodeJS/latest/bin/npm" "install" "beedle"
npm ERR! node v6.9.1
npm ERR! npm v3.10.8
npm ERR! code ENOSELF

npm ERR! Refusing to install beedle as a dependency of itself
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR! https://github.com/npm/npm/issues

npm ERR! Please include the following file with any support request:
npm ERR! /data/beedle/npm-debug.log

Steps to Reproduce the Problem

  1. npm install beedle

My Browser link(s)

Organizing large action.js files

Any advice for how to break up and organize large action.js files? I was thinking one file per view would be handy.
never mind figured out a nice system:
actions.js

import * as baseActions from '/store/actions/baseactions.js'
import * as taskActions from '/store/actions/taskActions.js'

export default {
...baseActions.default,
...taskActions.default
};

detecting state changes

If I set a deep property like this this:

  setExplorerSelectedBomId(state, payload) {
        state.explorer.bomId = payload;
        return state;
    },

my state change listeners do not get called.

If I add a kind of cache buster at the root of my state object, then it works.

    setExplorerSelectedBomId(state, payload) {
        state.foo=Date.now();
        state.explorer.bomId = payload;
        return state;
    },

It seems to work and is pretty painless, but I thought I would just check to see if this was a bug, or if there is a more proper way to push changes so that they get detected.

Render

Hey, mate! Don't you know how to update each list item in todo-list separately?
Using this technic, it's impossible without re-render all the items. Any ideas?

Create a basic console profiling setup

Similar to Redux, it'll be nice to create console groups for each action and/or mutation. I'm not sure how useful this is without a level of time travelling though. The original prototype had a basic setup, but it didn't work as well as I wanted it to.

Add esm "module" entrypoint for package

Any chance an esm module entry point can be added to package.json?

If I want to use a tool like @pikapkg/web without using a bundler it's not currently supported because it doesn't have an esm format output file that can be used with the module property of the package.

See this search result:
https://www.pika.dev/search?q=beedle

Possible to generate an esm format file and add this to the package as an option?

I can create a PR if this sounds OK.

Trigger callback with initialState

Expected Behavior

When I create a new Store with initialState I would expect my callbacks to be called with that initialState.

Actual Behavior

I have to trigger my callback once manually before subscribing a callback to the store

  1. Create a new Store with initialState
  2. Subscribe a callback to the Store
  3. Verify the callback has been called with initialState

Steps to Reproduce the Problem

  1. Create a new Store with initialState
  2. Subscribe a callback to the Store
  3. Verify the callback has not been called with initialState

Is this something you considered? Is it a good idea? Maybe it's a bad idea? ๐Ÿค”

example links not working

Expected Behavior

Examples links and documentation links don't work

Actual Behavior

Steps to Reproduce the Problem

My Browser link(s)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.