npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

egg-hatchery

v1.0.9

Published

> 🥚 Eggs are the new 🦆 ducks.

Downloads

12

Readme

Egg Hatchery 🐣

🥚 Eggs are the new 🦆 ducks.

import hatch from 'egg-hatchery';
import reduxEgg from 'redux-egg';
import counterEgg, { increment, getCount } from '@my/counter-egg';

test('counter egg increments in one', () => {
  const { store } = hatch(reduxEgg, counterEgg);
  store.dispatch(increment());
  expect(getCount(store.getState())).toBe(1);
});

Table of Content

Redux

Please, refer to the redux-egg to know how redux works with eggs.

  • https://github.com/drpicox/redux-egg/README.md

How an egg works?

An egg is function that receives an object with tools. Use those tools to create your eggs.

export const INCREMENT = '@my/counter/INCREMENT';
export const increment = () => ({ type: INCREMENT });
export const getCount = ({ '@my/counter': counter }) => counter;

function counterReducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT:
      return state + 1;
    default:
      return state;
  }
}

export default function counterEgg({ combineReducer }) {
  combineReducer('@my/counter', counterReducer);
}

How the redux-egg works?

The most simple redux-egg should look like:

import { createStore, combineReducers } from 'redux';

export default function storeEgg({ tool, breed }) {
  const reducers = {};

  tool('combineReducer', (name, reducer) => {
    reducers[name] = reducer;
  });

  breed('store', () => createStore(combineReducers(reducers)));
}

Why are eggs better than ducks?

REASON 1: Combine eggs and solve dependencies

Dependencies are hard with ducks. Each duck is independent and must be configured independently. The user of the duck must know its dependencies and any change will break an existing application.

But eggs solve the dependencies by themselves. Their use ages of human thinking about what comes first ducks or eggs and they conclude that dependency inversion is cool. If you have a dependency just use it.

import counterEgg, { INCREMENT } from 'counter-egg';

export const getParity = ({ '@my/parity': parity }) => parity;

function parityReducer(state = true, action) {
  switch (action.type) {
    case INCREMENT:
      return !state;
    default:
      return state;
  }
}

function parityEgg({ combineReducer }) {
  combineReducer('@my/parity', parityReducer);
}

export default [counterEgg, parityEgg];

And you can forgot to include the dependency in your app.

import hatch from 'egg-hatchery';
import storeEgg from 'store-egg';
import { increment } from '@my/counter-egg';
import parityEgg, { getParity } from '@my/parity-egg';

test('the parity changes with increment', () => {
  const { store } = hatch(storeEgg, counterEgg, parityEgg);
  store.dispatch(increment());
  expect(getParity(store.getState())).toBe(false);
});

Or you can include it. It is not repeated.

import hatch from 'egg-hatchery';
import storeEgg from 'store-egg';
import counterEgg, { increment, getCount } from '@my/counter-egg';
import parityEgg, { getParity } from '@my/parity-egg';

test('the parity is still correct when the counter egg is added twice', () => {
  const { store } = hatch(storeEgg, counterEgg, parityEgg);
  store.dispatch(increment());
  expect(getParity(store.getState())).toBe(false);
});

REASON 2: Thunks sucks

Well, not exactly. There is one and only one reason to use a thunk: you need the state before dispatching a new action from a component. If you remember the redux connect, it does not inject the state into dispatcher properties. The thunk middleware gives you access to that state. That limitation was because of performance. Nowadays, you can use hooks, but they are still more efficient if you use thunks.

The problem is the frequent use of thunks: launch subsequent actions to complement the current one. We were all thrilled with the ping pong example, but it was a lousy example. When we do these kinds of concatenated actions, we are looking for repercussions of the current action. In our duck, thanks to action creators, we can decouple and maintain it easily. The problem is, what happens when we want to intercept an action from an external duck? We need to use middleware, a redux observable, a saga, or something similar, but ducks are not ready for them. Like the reducers, if a duck needs a middleware or an equivalent, we have to prepare it manually.

The fiveEgg:

import counterEgg, { getCount, INCREMENT } from 'counter-egg';

export const FIVE = '@my/counter/FIVE';
export const getFives = ({ '@my/five': five }) => five;
const five = () => ({ type: FIVE });

function fiveReducer(state = 0, action) {
  switch (action.type) {
    case FIVE:
      return state + 1;
    default:
      return state;
  }
}

const fiveMiddleware = store => next => action => {
  next(action);
  switch (action.type) {
    case INCREMENT:
      if (getCount(store.getState()) % 5 === 0) store.dispatch(five());
    default:
  }
};

function fiveEgg({ combineReducer, addMiddleware }) {
  combineReducer('@my/five', fiveReducer);
  addMiddleware(fiveMiddleware);
}

export default [counterEgg, fiveEgg];

And how your program would look:

import hatch from 'egg-hatchery';
import storeEgg from 'store-egg';
import { increment } from '@my/counter-egg';
import fiveEgg, { getFives } from '@my/five-egg';

test('the five changes with increment', () => {
  const { store } = hatch(storeEgg, fiveEgg);
  store.dispatch(increment());
  store.dispatch(increment());
  store.dispatch(increment());
  store.dispatch(increment());
  store.dispatch(increment());
  expect(getFives(store.getState())).toBe(1);
});

REASON 3: They are still ducks

Well, they are almost ducks. There is only one change: instead of exporting by default, a reducer they export by default the egg. Everything else is the well-known old duck.

More than redux

The egg-hatchery is more about redux: it is a first-order dependency injection library. Look tests for more details at: