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

react-simple-flux

v1.4.2

Published

a simple -200line of code- Facebook Flux pattern implementation, just like Redux !.

Downloads

3

Readme

React Simple Flux

a simple -200line of code- Facebook Flux pattern implementation, just like Redux !.

it use React's 16.3 > new Contect Api mix it with Event Subscription model,to focus on separation of concerns while building modern complex react applications !

Components

just like redux we have

Action creator

it create an action (simple object with a type) and send it to the reducer.

Reducer

receive an action and return a new version of App State

Connected Component / smart+or-dump components

someone who responde to user actions by emitting an event !.

Example Usage

example Login Component

login.js

  class LoginContainer extends React.PureComponent{

    componentDidMount(){
      this.listeners = [
        this.props.listen("LOGIN_START", ()=>this.setState({loading:true})),
        this.props.listen("LOGIN_END", ()=>this.setState({loading:false})),
        this.props.listen("LOGIN_FAILED", (eventName, error)=>this.setState({error})),
      ];
    }

    componentWillUnMount(){
      this.listeners.map(un=>un());
    }

    attemptLogin = (username, password) => {
      this.props.emit("ATTEMPT_LOGIN", {username, password})
    }

    render(){
      return <div>

          {this.state.loading && <Spinner> Please wait ... </Spinner>}

          {this.state.error && <Error> {this.state.error.reason} </Error>}

          <LoginForm
            onSubmit={this.attemptLogin}
            disabled={this.state.loading}
          />

      </div>
    }

  }

  LoginContainer.stateToProps = (store, selectors) => ({
    currentUser: selectors.auth.getCurrentUser(store),
  })

  export default withCore(LoginContainer);

above you will notice few things.

  • using listen and emit, makes the UI very clean !
  • this.props.listen return a function that unsubscribe listerner, so we call this function on unmount to clear all subscriptions.
  • we will not do any validation in UI, its the SDK job to do it and emit LOGIN_FAILED if it fail, this allow for maximum code sharing between project.
  • RULE OF THUMB: if its not Enviroment dependent code, try to move it to SDK package.

example SDK code

  // selectors.js
  export const getCurrentUser =  store => store[types.mountKey] || reducer.initialState;

  // reducer.js
  export function reducer(state=reducer.initalState;, action){
    if(action.type === 'LOGIN_SUCCESS'){
      return action.data;
    }
    return state
  }
  reducer.initalState = {};
  reducer.eventName = ['LOGIN_SUCCESS']; // only get called if action.type === 'LOGINSUCCESS'


  // action.js
  async function loginActionCreator(event, data, emit){

    // if(event !== 'LOGIN_ATTEMPT')return; // we dont need this, because we used eventName bellow.

    // step 1: validate Data;
    // -----------------------

    if( !data.username )
      return emit('LOGIN_FAILED',{message:'username is required'});
    if( !data.password )
      return emit('LOGIN_FAILED',{message:'password is required'});

    // step 2: trying to login
    // -----------------------

    emit('LOGIN_START');
    let user;
    try{
      user = await api.post('/login', data);
    }catch(e){
      emit('LOGIN_FAILED',{message:'username or password doesnot match'});
    }
    emit('LOGIN_END');

    if(user && user.data.token){
      // login success, and data has my token

      //step 3: return action to reducer to change appState
      // --------------------------------------------------
      return {
        type: 'LOGIN_SUCCESS',
        data: user.data,
      }
    }
  }
  loginActionCreator.eventName = 'LOGIN_ATTEMPT'; // only responde to 'LOGIN_ATTEMPT'

Thats It, now you have your smart actionCreator inside your ./sdk folder contain all business logic, and you have kept your ui logic clean and as minimal as possible.

this allow you to share ./sdk folder with your mobile/web/other project easily, you can pack it into its own npm package and simply npm install it. MAXIMUM code share :)

So how it works ?

we give Action creators and connected components some super powers. so Typical life cycle is as so.

  • On UI part User Create An Event(click on button, or scroll, or whatever)
  • Component Respond to that action by calling this.props.emit("EVENT_NAME",eventData);
  • On SDK side Actions listening for this "EVENT_NAME" will get triggered -asyncly-
  • Action can Do 2 things now -since this can be async function-
    • return plain action -an object with type: prop-
    • emit another event (which will reset this cycle)
  • Reducer receive event returned by ActionCreator and change state !
  • All connected components get notified !

Why we think this is better ?

  • UI designers now need to worry about just Emitting Events, no more complex bindActionCreator, or magical functions gets imported and injected into our component
  • App developers can develope whole SDK in conjunction with backend, without worrying about front end or presentation.
  • Its much faster -and safer- to run only reducers who subscribe to an event, not all reducers in chain !

Example

index.js

import { actions, rootReducer, selectors } from "./sdk";

// or whatever logic you want to persist !
const loadedFromDisk = JSON.parse(localStorage.myApp || "{}");
const saveToDisk = state =>
  localStorage.setItem("myApp", JSON.stringify(state));

React.Render(
  <Provider
    reducer={rootReducer}
    actions={actions}
    selectors={selectors}
    onChange={saveToDisk}
    initalState={loadedFromDisk}
  >
    <MyApp />
  </Provider>,
  rootEl
);

your sdk/index.js would look something like

import Users from "./auth";
import Todos from "./todos";
import { combineReducers } from "react-simple-flux";

export const rootReducer = combineReducers({
  [User.types.mountKey]: Users.reducer,
  [Todos.types.mountKey]: Todos.reducer
});

export const selectors = [...Users.selectors, ...Todos.selectors];

export const actions = [...Users.actions, ...Todos.actions];

every time you create a new module inside sdk folder, just add a referenec for it in you sdk/index.js file so that it would be included in your app.

A module folder recommended folder structure would be

  • index.js
  • actions.js
  • selectors.js
  • types.js
  • reducer.js

Recommendation: create a small node/terminal tool that help you generate such boilerplate, for a better development experience !, we included a generator.js inside our ./src folder as an example.

index.js

import reducer from "./reducer";
import actions from "./actions";
import * as selectors from "./selectors";
import * as types from "./types";

export default {
  reducer,
  actions,
  selectors,
  config
};

reducer.js

import { ONLOAD } from "./types";
const initialState = {};

function userReducer(state = initialState, action, store) {
  return state;
}
userReducer.eventName = [ONLOAD];
userReducer.initialState = initialState;

export default userReducer;

actions.js

import { ONLOAD } from "./types";
// import API from "../../api";

async function loadAction(eventName, data, emit, getState) {
  // - i can await an api call before i return !

  // -OR i can also re-emit an event
  // but careful not fall into a loop !

  // emit("API_STARTING",data);
  // const data = api.get();
  // emit("API_END",data);

  // very useful for showing spinner !

  return null; // since no object with {type:''} this will not trigger any reducer.
  // useful when api fail, no need to trigger reducers..

  // example if api success and you need to call reducers
  // return {
  //   type: ONLOAD,
  //   data: whatever
  // }
}
loadAction.eventName = ONLOAD;

export default [loadAction];

selectors.js

  import * as types from './types';
  import reducer from './reducer';

  export const getTodos = store => store[types.mountKey] || reducer.initialState;

Todo

  • improve HOC function, may be implementing shouldComponentUpdate if its proved to be worth it.
  • consider moving logic to its own worker.
  • consider enabling remote Event sourcing keep state tree on remote host !.