1.0.4 • Published 7 years ago

weeflux v1.0.4

Weekly downloads
4
License
MIT
Repository
github
Last release
7 years ago

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'));
1.0.4

7 years ago

1.0.3

7 years ago

1.0.2

7 years ago

1.0.0

7 years ago

1.0.1

7 years ago