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();
const actions = {
add: 'add',
remove: 'remove',
textChanged: 'text-changed',
rollback: 'rollback',
profileLoaded: 'loaded',
profileFailed: 'failed',
profileLoading: 'loading'
};
const immerMiddleware = next => context => {
return produce(context.getState(), draft =>
next({
...context,
getState() {
return draft;
}
})
);
};
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;
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 => {
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);
};
middleware(immerMiddleware);
middleware(asyncMiddleware);
middleware(logMiddleware);
middleware(timemachineMiddleware);
flux({
todos: {},
ids: [],
text: ''
});
subscribe(console.log, 'state-changed');
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;
}
});
flux(actions.add, 'Task 1');
flux(actions.add, 'Task 2');
flux(actions.add, 'Task 3');
hoc('stateToProps', stateToProps => Component => {
return class WrappedComponent extends React.Component {
componentDidMount() {
this.unsubscribe = subscribe(() => !this.unmount && this.forceUpdate());
}
componentWillUnmount() {
this.unmount = true;
this.unsubscribe();
}
render() {
const props = stateToProps(flux(), this.props);
return <Component {...props} />;
}
};
});
flux(partial('text', actions.textChanged, (state, { payload }) => payload));
flux(
partial(
'profile',
[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) => (
<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'));