weeflux

1.0.4 • Public • Published

Weeflux

A state management based on flux architecture
Weeflux in actions

import React from 'react';
import { render } from 'react-dom';
import flux, { clean, middleware, subscribe, hoc } from './weeflux';
import partial from './weeflux-partial-reducer';
import uuid from 'uuid';
import produce from 'immer';
 
clean();
 
// define some actions
const actions = {
  add: 'add',
  remove: 'remove',
  textChanged: 'text-changed',
  rollback: 'rollback',
  profileLoaded: 'loaded',
  profileFailed: 'failed',
  profileLoading: 'loading'
};
 
// inject immer for state updating
const immerMiddleware = next => context => {
  return produce(context.getState(), draft =>
    next({
      ...context,
      getState() {
        return draft;
      }
    })
  );
};
 
// logging middleware
const logMiddleware = next => context => {
  const before = context.getState();
  console.log('before', context.action, before);
  const after = next(context);
  console.log('after', context.action, after);
  return after;
};
 
const history = [];
const timemachineMiddleware = next => context => {
  if (context.action === actions.rollback) {
    const historyIndex = context.payload;
    // clean history
    history.splice(historyIndex + 1, history.length);
 
    return next({
      ...context,
      payload: undefined,
      getState() {
        return history[historyIndex] || {};
      }
    });
  }
  const before = context.getState();
  if (before !== history[history.length - 1]) {
    history.push(before);
  }
 
  return next(context);
};
 
const asyncMiddleware = next => context => {
  // is promise
  if (context.payload && context.payload.then) {
    const { defaultValue, success, failure } = context.extra[0] || {};
    context.payload.then(
      response => flux(success, { status: 'success', response }),
      error => flux(failure, { status: 'failure', error })
    );
 
    return next({
      ...context,
      payload: {
        status: 'start',
        response: defaultValue
      }
    });
  }
  return next(context);
};
 
// install middlewares
 
middleware(immerMiddleware);
middleware(asyncMiddleware);
middleware(logMiddleware);
middleware(timemachineMiddleware);
 
// init state
flux({
  todos: {},
  ids: [],
  text: ''
});
 
// subscription
subscribe(console.log, 'state-changed');
 
// reducer for todo actions
flux((state, { action, payload }) => {
  switch (action) {
    case actions.add:
      const id = uuid();
      state.todos[id] = { text: payload };
      state.ids.push(id);
      break;
    case actions.remove:
      delete state.todos[payload];
      state.ids.splice(state.ids.indexOf(payload), 1);
      break;
  }
});
 
// dispatch some actions
flux(actions.add, 'Task 1');
flux(actions.add, 'Task 2');
flux(actions.add, 'Task 3');
 
// register hoc, this hoc will be activated if component options contains stateToProps prop
hoc('stateToProps', stateToProps => Component => {
  return class WrappedComponent extends React.Component {
    componentDidMount() {
      // subscribe method returns a function which can be use to unsubscribe subscription later
      this.unsubscribe = subscribe(() => !this.unmount && this.forceUpdate());
    }
 
    componentWillUnmount() {
      this.unmount = true;
      this.unsubscribe();
    }
    render() {
      // map state to props
      const props = stateToProps(flux(), this.props);
      return <Component {...props} />;
    }
  };
});
 
// reducer for text
flux(partial('text', actions.textChanged, (state, { payload }) => payload));
 
flux(
  partial(
    'profile',
    // handle multiple actions with one reducer
    [actions.profileLoaded, actions.profileFailed, actions.profileLoading],
    (state, { payload }) => payload
  )
);
 
const fetchData = url =>
  new Promise((resolve, reject) => {
    setTimeout(
      () =>
        fetch(url)
          .then(res => res.text())
          .then(resolve, reject),
      3000
    );
  });
 
flux(
  actions.profileLoading,
  fetchData('https://api.github.com/users/linq2js'),
  {
    defaultValue: { thisIsFirstProfile: true },
    failure: actions.profileFailed,
    success: actions.profileLoaded
  }
);
 
const stateToProps = state => ({ text: state.text });
 
const Input = flux({ stateToProps }, props => (
  <input
    type="text"
    value={props.text}
    onChange={e => flux(actions.textChanged, e.target.value)}
  />
));
 
const Output = flux({ stateToProps }, props => <div>{props.text}</div>);
 
const History = flux({ stateToProps: () => ({ history }) }, props => (
  <div>
    <strong>History</strong>
    <ol>
      {props.history.map((item, index) => (
        // display history item
        <li key={index} onClick={() => flux(actions.rollback, index)}>
          {JSON.stringify(item)}
        </li>
      ))}
    </ol>
  </div>
));
 
const App = () => (
  <div>
    <Input />
    <Output />
    <History />
  </div>
);
 
render(<App />, document.getElementById('root'));

Package Sidebar

Install

npm i weeflux

Weekly Downloads

1

Version

1.0.4

License

MIT

Unpacked Size

37.1 kB

Total Files

13

Last publish

Collaborators

  • linq2js