Giter VIP home page Giter VIP logo

stockroom's Introduction

stockroom
stockroom
npm travis

Stockroom

Offload your store management to a worker.

Stockroom seamlessly runs a Unistore store (and its actions) in a Web Worker, setting up optimized bidirectional sync so you can also use & subscribe to it on the main thread.

  • Easy same API as unistore - a simple add-on
  • Opt-in centralized actions with the option of running on the main thread
  • Convenient action selector shorthand - no action creator needed for simple actions
  • Gracefully degrades - feature-detect Worker support and fall back to stockroom/inline

Table of Contents

Install

Stockroom requires that you install unistore (300b) as a peer dependency.

npm install --save unistore stockroom

Usage

We'll have two files: index.js and worker.js. The first is what we import from our app, so it runs on the main thread - it imports our worker (using worker-loader or workerize-loader) and passes it to Stockroom to create a store instance around it.

index.js:

import createStore from 'stockroom'
import StoreWorker from 'worker-loader!./worker'

let store = createStore(new StoreWorker())

let increment = store.action('increment')
store.subscribe(console.log)

// Let's run a registered "increment" action in the worker.
// This will eventually log a state update to the console - `{ count: 1 }`
increment()

The second file is our worker code, which runs in the background thread. Here we import Stockroom's worker-side "other half", stockroom/worker. This function returns a store instance just like createStore() does in Unistore, but sets things up to synchronize with the main/parent thread. It also adds a registerActions method to the store, which you can use to define globally-available actions for that store. These actions can be triggered from the main thread by invoking store.action('theActionName') and calling the function it returns.

worker.js:

import createStore from 'stockroom/worker'

let store = createStore({
  count: 0
})

store.registerActions( store => ({
  increment: ({ count }) => ({ count: count+1 })
}) )

export default store  // if you wish to use `stockroom/inline`

API

module:stockroom

The main stockroom module, which runs on the main thread.

createStore

Given a Web Worker instance, sets up RPC-based synchronization with a WorkerStore running within it.

Parameters

  • worker Worker An instantiated Web Worker (eg: new Worker('./store.worker.js'))

Examples

import createStore from 'stockroom'
import StoreWorker from 'worker-loader!./store.worker'
let store = createStore(new StoreWorker)

Returns Store synchronizedStore - a mock unistore store instance sitting in front of the worker store.

module:stockroom/inline

Used to run your whole store on the main thread. Useful non-worker environments or as a fallback.

createInlineStore

For SSR/prerendering, pass your exported worker store through this enhancer to make an inline synchronous version that runs in the same thread.

Parameters

  • workerStore WorkerStore The exported store instance that would have been invoked in a Worker

Examples

let store
if (SUPPORTS_WEB_WORKERS === false) {
	let createStore = require('stockroom/inline')
	store = createStore(require('./store.worker'))
}
else {
	let createStore = require('stockroom')
	let StoreWorker = require('worker-loader!./store.worker')
	store = createStore(new StoreWorker())
}
export default store

Returns Store inlineStore - a unistore instance with centralized actions

module:stockroom/worker

The other half of stockroom, which runs inside a Web Worker.

createWorkerStore

Creates a unistore instance for use in a Web Worker that synchronizes itself to the main thread.

Parameters

  • initialState Object Initial state to populate (optional, default {})

Examples

import createWorkerStore from 'stockroom/worker'
let initialState = { count: 0 }
let store = createWorkerStore(initialState)
store.registerActions({
	increment(state) {
		return { count: state.count + 1 }
	}
})

Returns WorkerStore workerStore (enhanced unistore store)

freeze

Queue all additional processing until unfrozen. freeze/unfreeze manages a cumulative lock: unfreeze must be called as many times as freeze was called in order to remove the lock.

unfreeze

Remove a freeze lock and process queued work.

License

MIT License © Jason Miller

stockroom's People

Contributors

developit avatar dhenson02 avatar johnrees avatar kanaye 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stockroom's Issues

Error when updating state from null to an object.

When updating a states property from a primitiv value (e.g. null) null to an object.
The following line tries to access an property on the primitiv value and therefore throws an error.

update[i] = diffOne(obj[i], prev[i]);

Here an example.

I probably also PR for this one later.

Edit: It's only null because typeof null === 'object'.

Window is not defined

Hello there, this my first time using web workers and this module, I have a webpack config for bundling to JavaScript files index.js & worker.worker.js.

This is what my webpack config looks like

const { resolve } = require('path')

const entryPath = resolve(`${__dirname}/src`)
const outPath = resolve(`${__dirname}/dist`)

module.exports = {
    entry: resolve(`${entryPath}/index.js`),
    output: {
        path: outPath,
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.worker\.js$/,
                use: { loader: 'worker-loader' }
            },
            {
                test: /\.js$/,
                include: entryPath,
                loader: 'babel-loader',
                query: {
                    presets: [
                        [
                            'env',
                            {
                                targets: {
                                    node: '6.11.2'
                                }
                            }
                        ]
                    ]
                }
            },
        ]
    }
}

In the the two .js files are the demo exmpales from the repo. I am not user what I did wrong or did mis a step.

Thank you

Access to the store in components

Hey,

Simple question here, how can I access to the store in my components ?

In Unistore I would use connect() but here it seems that I have no access to actions because they run in a separate worker.

Thanks.

Store properties undefined on "first render"

For a reason that I do not know, my store is undefined on "first render".

In that capture of devtools console, I log a property of my store.
It is first undefined then HMR comes and then the property is defined.

image

import { Component } from 'preact';
import { connect } from 'unistore/preact';

@connect(['toast'])
class Toast extends Component {
  render ({toast}, {}) {
    console.log(toast);
    return (
      <div class="toast-container">
        <div class="toast">
          {/*toast.messages.map((message, index) => (
            <p class="toast--content" key={index}>{message}</p>
          ))*/}
        </div>
      </div>
    )
  }
}

export default Toast;
// store/worker.js
import createStore from 'stockroom/worker';
import idbKeyVal from 'idb-keyval';
import Constants from '../constants';

const store = createStore({
  user: {
    userId: '',
    email: '',
    avatar: ''
  },
  toast: {
    messages: [],
    show: false
  }
});

// globally available actions
store.registerActions(store => ({
  toasting ({toast}, messages) {
    return {
      toast: {
        messages,
        show: true
      }
    };
  }
}));
// index.js
import createStore from 'stockroom';
import storeWorker from 'workerize-loader!./store/worker';
import { Provider, connect } from 'unistore/preact';
import devtools from 'unistore/devtools';

const production = process.env.NODE_ENV === 'production';
const store = production ? createStore(storeWorker()) : devtools(createStore(storeWorker()));

import './style/index.scss';
import App from './components/app';

export default () => (
  <Provider store={store}>
    <App />
  </Provider>
);

I don't have that problem when I only use unistore without stockroom.

Can't delete nested object from store

When used with unistore is impossible to delete / remove a nested object or property.
See the following example ( This code only works using unistore without stockroom )

// Store
const state = {
  collections: {
     items: {
        a: { ... },
        b: { ... },
        c: { ... },
     }
    // ...
  }
 // ...
}

// Action
removeItem({ collections }, id) {
      let items = collections.items;
      // Remove item
      if (items[id]) {
        const { [id]: removed, ...rest } = items;
        items = rest;
      }
    return { collections: { ...collections, items } };
},

Unistore only example (working demo)

Edit Unistore Demo

TypeScript Definitions

Love the idea behind stockroom, offers a great way to easily push non-UI work off the UI thread. The one thing stopping me from adopting it however is the lack of proper TypeScript support (package.json does refer to a file, however it does not exist).

I'm going to have a go at creating the necessary definitions. If things go to plan, I should have a PR for it up soon.

Unistore when used with stockroom not updating as expected

Suppose I have an initial state like:

data: {}

Make an action to return

data: [
	{
	    "userId": 1,
	    "id": 1,
	    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
	    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
	},
	...
]

This will not cause the store to update. In fact no errors are thrown. It just silently fails

Any idea what might be wrong.

This also happens when the initial state is null then it is updated with some data and then set back to null. The last step does not update the store.

Found #9 but its for the reverse and it works. Object to null is not working.

If I get time, I'll make an example repo to better show the issue

Update 1:

Looks like unistore/stockroom is internally using Object.assign or push/shift to set/update the states. This might be the cause of the issue

Update 2:

This issue occurs when using unistore with stockroom. Normally it works as expected

getting below error when trying to compile,kindly help as i am new to this technology

Error: Avatar(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
▶ 17 stack frames were collapsed.
Module../src/index.js
C:/Users/Administrator/react2019ayushi/reactproj/src/index.js:10
7 | import Avatar from './Avatar';
8 |
9 |

10 | ReactDOM.render(, document.getElementById('root'));
11 |
12 |
13 | serviceWorker.unregister();
View compiled

Avatar.js:

import React, { Component } from 'react';
import './Avatar.css';

import Avatarlist from './Avatarlist';
import 'tachyons';

const Avatar = (props) => {

class Avatar extends Component{
    
    constructor(){
        super();
        this.state = {
            name : "Welcome to avatar world"
        }


    }
    namechange(){
        this.setState({
            name : "Subscribe to ayushi"
        })
    }

    render() {
        const avaratlistarray = [
            {
                id:1,
                name:"ayushi",
                work:"web developer"
            },
            {
                id:2,
                name:"ishita",
                work:"front-End developer"
            },
        
        ] 
    
        const arrayavaratcard =  avaratlistarray.map( (avatarcard, i) => {
          return <Avatarlist key={i} id={avaratlistarray[i].name}
                        name={avaratlistarray[i].name}
                        work={avaratlistarray[i].work}/>
     } )
    return (  
    <div className="mainpage">
    <h1>{this.state.name}</h1>
    {arrayavaratcard}
    <button onClick= { () => this.namechange() }> Subscribe </button>
  </div>
  )
}
}

}
export default Avatar;

Event origin check?

I was reading Tavis Ormandy's tweet about Grammarly's chrome extension vulnerability today. It made me think about stockroom since it is using the same postMessage interface. Report can be found here. Other reference that I found useful: this and this

Support for redux?

It would be good if stockroom provided a way to insert instance of state manager as an input config.
do you have any plan for it in the roadmap or stockroom is meant to work with unistore?

Using the inline worker emits errors in browser context.

TypeError
Failed to execute 'postMessage' on 'Window': 2 arguments required, but only 1 present.

is thrown in the normal browsers window context because window.postMessage expects 2 arguments and is called with one here:

if (typeof postMessage==='function') postMessage(sendQueue);

The easiest way to fix this, is to add extend the condition to be:
if (typeof postMessage === 'function' && postMessage.length === 1)

I'll probably PR later, but I'm documenting my findings while I'm debugging an other error.

Edit: Example emitting this error: https://codesandbox.io/s/10r1j4z06l

Differences in the inline and the normal way

Hi,
I expect a lot of bug with stockroom (or maybe I using it wrong ?)

I extract a little part of my webapp to inspect it, but I encounter an (other) probleme.
To reproduce the bugs : https://github.com/Buom01/stockroom-bug-demo (created with preact-cli)

the subscribe method

In this case, the "normal" usage, the store have not got the subscribe method defined:

import createStore from 'stockroom/inline'
import StoreWorker from 'workerize-loader!./worker.store'
let store = createStore(new StoreWorker())
console.log(store.subscribe) // undefined, WHY ?

While in the "inline" usage, the method subscribe is defined normally:

import createStore from 'stockroom/inline'
let store = createStore(require('./worker.store').default)
console.log(store.subscribe) // defined as a function

A probleme of shared store in the "inline" mod

Sorry because I can not totally reproduce the bug that I want to resolve due to the probleme with the subscribe method.
The probleme can be showed partially in the demo provided: I add only one document to each store, but in the inline mod, the store are mixed and finally they share their data. Then, they have both the same data while they should be isolated like in the worker mod. (Confirmed in my local project)

Tested in both Chromium(Version 67.0.3396.79 (Build de développement) Fedora Project (64 bits)) and Firefox(61.0) (latest)

Nothing was returned from render error while trying to implement with React

I am trying to implement the basic counter using React but getting the below error

invariant.js:42 Uncaught Error: p(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
at invariant (invariant.js:42)
at reconcileChildFibers (react-dom.development.js:7688)
at reconcileChildrenAtExpirationTime (react-dom.development.js:7756)
at reconcileChildren (react-dom.development.js:7747)
at mountIndeterminateComponent (react-dom.development.js:8075)
at beginWork (react-dom.development.js:8221)
at performUnitOfWork (react-dom.development.js:10224)
at workLoop (react-dom.development.js:10288)
at HTMLUnknownElement.callCallback (react-dom.development.js:542)
at Object.invokeGuardedCallbackDev (react-dom.development.js:581)

Redux

Maybe it's a silly question, but how to use stockroom with redux?

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.