this.handleSave(todo.id, text)} />
42 | );
43 | } else {
44 | element = (
45 |
46 | markTodo(todo.id)} />
50 |
53 |
56 | );
57 | }
58 |
59 | return (
60 |
64 | {element}
65 |
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/examples/todomvc/components/TodoTextInput.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import classnames from 'classnames';
3 |
4 | export default class TodoTextInput extends Component {
5 | static propTypes = {
6 | onSave: PropTypes.func.isRequired,
7 | text: PropTypes.string,
8 | placeholder: PropTypes.string,
9 | editing: PropTypes.bool,
10 | newTodo: PropTypes.bool
11 | };
12 |
13 | constructor(props, context) {
14 | super(props, context);
15 | this.state = {
16 | text: this.props.text || ''
17 | };
18 | }
19 |
20 | handleSubmit(e) {
21 | const text = e.target.value.trim();
22 | if (e.which === 13) {
23 | this.props.onSave(text);
24 | if (this.props.newTodo) {
25 | this.setState({ text: '' });
26 | }
27 | }
28 | }
29 |
30 | handleChange(e) {
31 | this.setState({ text: e.target.value });
32 | }
33 |
34 | handleBlur(e) {
35 | if (!this.props.newTodo) {
36 | this.props.onSave(e.target.value);
37 | }
38 | }
39 |
40 | render() {
41 | return (
42 |
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/examples/todomvc/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_TODO = 'ADD_TODO';
2 | export const DELETE_TODO = 'DELETE_TODO';
3 | export const EDIT_TODO = 'EDIT_TODO';
4 | export const MARK_TODO = 'MARK_TODO';
5 | export const MARK_ALL = 'MARK_ALL';
6 | export const CLEAR_MARKED = 'CLEAR_MARKED';
7 |
--------------------------------------------------------------------------------
/examples/todomvc/constants/TodoFilters.js:
--------------------------------------------------------------------------------
1 | export const SHOW_ALL = 'show_all';
2 | export const SHOW_MARKED = 'show_marked';
3 | export const SHOW_UNMARKED = 'show_unmarked';
4 |
--------------------------------------------------------------------------------
/examples/todomvc/constants/WireActions.js:
--------------------------------------------------------------------------------
1 | export const SET_STATE = 'SET_STATE';
--------------------------------------------------------------------------------
/examples/todomvc/containers/JsonApp.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { createRedux, composeStores, compose } from 'redux';
3 | import {Connector} from 'react-redux';
4 | import * as reducers from '../reducers';
5 |
6 | export default class JsonApp extends Component {
7 | render() {
8 | return (
9 | { todos }}>
10 | {this.renderChild}
11 |
12 | );
13 | }
14 |
15 | renderChild(data) {
16 | return (
17 |
18 | {JSON.stringify(data, null, 2)}
19 |
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/examples/todomvc/containers/TodoApp.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { bindActionCreators } from 'redux';
3 | import { Connector } from 'react-redux';
4 | import Header from '../components/Header';
5 | import MainSection from '../components/MainSection';
6 | import * as TodoActions from '../actions/TodoActions';
7 |
8 | export default class TodoApp extends Component {
9 | render() {
10 | return (
11 | ({ todos: state.todos })}>
12 | {this.renderChild}
13 |
14 | );
15 | }
16 |
17 | renderChild({ todos, dispatch }) {
18 | const actions = bindActionCreators(TodoActions, dispatch);
19 | return (
20 |
21 |
24 | );
25 | }
26 | }
--------------------------------------------------------------------------------
/examples/todomvc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Redux TodoMVC
4 |
5 |
6 |
7 |
8 |
Master -> Slave
9 |
13 |
17 |
18 |
29 |
30 |
31 |
32 |
57 |
58 |
--------------------------------------------------------------------------------
/examples/todomvc/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import {createSubscribeOnStateStore} from '../../src';
4 | import 'todomvc-app-css/index.css';
5 | import TodoApp from './containers/TodoApp';
6 | import * as reducers from './reducers';
7 | import R from 'ramda';
8 | import pure from 'react-pure-component';
9 | import * as redux from 'redux';
10 |
11 | const SET_STATE = "@@@@SET_STATE";
12 | const setState = (state) =>({type: SET_STATE, state});
13 |
14 | const oldTodos = reducers.todos;
15 |
16 | reducers.todos = (state, action)=>{
17 | switch (action.type) {
18 | case SET_STATE:
19 | return action.state.todos;
20 | default:
21 | return oldTodos(state, action)
22 | }
23 | };
24 |
25 | const store1 = createSubscribeOnStateStore(redux, SET_STATE, reducers);
26 | const store2 = createSubscribeOnStateStore(redux, SET_STATE, reducers);
27 | const store3 = createSubscribeOnStateStore(redux, SET_STATE, reducers);
28 | const store4 = createSubscribeOnStateStore(redux, SET_STATE, reducers);
29 |
30 | const dispatchState = (store)=>(state)=>store.dispatch(setState(state));
31 |
32 | store1.subscribeOnState((data)=>dispatchState(store2)(data));
33 | //app2.subscribeOnState((data)=>dispatchState(store2)(data)));
34 | store3.subscribeOnState((data)=>dispatchState(store4)(data));
35 | store4.subscribeOnState((data)=>dispatchState(store3)(data));
36 |
37 | const createApp = R.curry((UserApp, store) => pure(()=>(
38 |
39 | {() => }
40 |
41 | )));
42 |
43 | const createTodoApp = createApp(TodoApp);
44 |
45 | const App1 = createTodoApp(store1);
46 | const App2 = createTodoApp(store2);
47 | const App3 = createTodoApp(store3);
48 | const App4 = createTodoApp(store4);
49 |
50 | React.render(
51 | ,
52 | document.getElementById('root1')
53 | );
54 |
55 | React.render(
56 | ,
57 | document.getElementById('root2')
58 | );
59 |
60 | React.render(
61 | ,
62 | document.getElementById('root3')
63 | );
64 |
65 | React.render(
66 | ,
67 | document.getElementById('root4')
68 | );
69 |
--------------------------------------------------------------------------------
/examples/todomvc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "todomvc",
3 | "version": "0.0.0",
4 | "description": "TodoMVC example for redux",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/gaearon/redux-devtools.git"
12 | },
13 | "keywords": [
14 | "react",
15 | "reactjs",
16 | "hot",
17 | "reload",
18 | "hmr",
19 | "live",
20 | "edit",
21 | "webpack",
22 | "flux",
23 | "todomvc"
24 | ],
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/gaearon/redux-devtools/issues"
28 | },
29 | "homepage": "https://github.com/gaearon/redux-devtools#readme",
30 | "devDependencies": {
31 | "babel-core": "^5.6.18",
32 | "babel-loader": "^5.1.4",
33 | "node-libs-browser": "^0.5.2",
34 | "raw-loader": "^0.5.1",
35 | "react-hot-loader": "^1.2.7",
36 | "style-loader": "^0.12.3",
37 | "todomvc-app-css": "^2.0.1",
38 | "webpack": "^1.9.11",
39 | "webpack-dev-server": "^1.9.0"
40 | },
41 | "dependencies": {
42 | "classnames": "^2.1.2",
43 | "jsondiffpatch": "^0.1.33",
44 | "ramda": "^0.17.1",
45 | "react": "^0.13.3",
46 | "react-pure-component": "^0.1.0",
47 | "react-redux": "^0.2.1",
48 | "redux": "^1.0.0-rc",
49 | "redux-thunk": "^0.1.0",
50 | "todomvc-app-css": "^2.0.1"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/examples/todomvc/reducers/index.js:
--------------------------------------------------------------------------------
1 | export { default as todos } from './todos';
2 |
--------------------------------------------------------------------------------
/examples/todomvc/reducers/todos.js:
--------------------------------------------------------------------------------
1 | import { ADD_TODO, DELETE_TODO, EDIT_TODO, MARK_TODO, MARK_ALL, CLEAR_MARKED } from '../constants/ActionTypes';
2 | import { SET_STATE } from '../constants/WireActions';
3 |
4 | const initialState = [{
5 | text: 'Use Redux',
6 | marked: false,
7 | id: 0
8 | }];
9 |
10 | export default function todos(state = initialState, action) {
11 | switch (action.type) {
12 | case ADD_TODO:
13 | return [{
14 | id: (state.length === 0) ? 0 : state[0].id + 1,
15 | marked: false,
16 | text: action.text
17 | }, ...state];
18 |
19 | case DELETE_TODO:
20 | return state.filter(todo =>
21 | todo.id !== action.id
22 | );
23 |
24 | case EDIT_TODO:
25 | return state.map(todo =>
26 | todo.id === action.id ?
27 | {...todo, text: action.text} :
28 | todo
29 | );
30 |
31 | case MARK_TODO:
32 | return state.map(todo =>
33 | todo.id === action.id ?
34 | {...todo, marked: !todo.marked} :
35 | todo
36 | );
37 |
38 | case MARK_ALL:
39 | const areAllMarked = state.every(todo => todo.marked);
40 | return state.map(todo => ({
41 | ...todo,
42 | marked: !areAllMarked
43 | }));
44 |
45 | case CLEAR_MARKED:
46 | return state.filter(todo => todo.marked === false);
47 |
48 | default:
49 | return state;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/todomvc/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true,
9 | stats: {
10 | colors: true
11 | }
12 | }).listen(3000, 'localhost', function (err) {
13 | if (err) {
14 | console.log(err);
15 | }
16 |
17 | console.log('Listening at localhost:3000');
18 | });
19 |
--------------------------------------------------------------------------------
/examples/todomvc/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | console.log(path.resolve(__dirname + '/../../src'))
5 | module.exports = {
6 | entry: [
7 | 'webpack-dev-server/client?http://localhost:3000',
8 | 'webpack/hot/only-dev-server',
9 | './index'
10 | ],
11 | output: {
12 | path: path.join(__dirname, 'dist'),
13 | filename: 'bundle.js',
14 | publicPath: '/static/'
15 | },
16 | plugins: [
17 | new webpack.HotModuleReplacementPlugin(),
18 | new webpack.NoErrorsPlugin()
19 | ],
20 | resolve: {
21 | extensions: ['', '.js']
22 | },
23 | module: {
24 | loaders: [{
25 | test: /\.js$/,
26 | loaders: ['react-hot', 'babel'],
27 | exclude: /node_modules/,
28 | include: __dirname
29 | },{
30 | test: /\.js$/,
31 | loaders: ['babel'],
32 | exclude: /node_modules/,
33 | include: path.resolve(__dirname + '/../../src')
34 | }, {
35 | test: /\.css?$/,
36 | loaders: ['style', 'raw'],
37 | include: __dirname
38 | }]
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Redux TodoMVC
4 |
5 |
6 |
7 |
8 |
9 |
Server -> Client
10 |
14 |
18 |
19 |
30 |
31 |
32 |
33 |
58 |
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-remote",
3 | "version": "0.0.0",
4 | "description": "Most importantly that I am not resend all actions but listen store updates and send it to another redux instance which updates itself.",
5 | "main": "index.js",
6 | "directories": {
7 | "example": "examples"
8 | },
9 | "scripts": {
10 | "clean": "rimraf lib",
11 | "build": "babel src --out-dir lib",
12 | "lint": "eslint src test examples",
13 | "test": "SET NODE_ENV=test mocha --compilers js:babel/register --recursive",
14 | "test:watch": "SET NODE_ENV=test mocha --compilers js:babel/register --recursive --watch",
15 | "test:cov": "babel-node ./node_modules/.bin/isparta cover ./node_modules/.bin/_mocha -- --recursive",
16 | "prepublish": "npm run lint && npm run test && npm run clean && npm run build",
17 | "demo":"bash ./demo.sh"
18 | },
19 | "devDependencies": {
20 | "babel": "^5.5.8",
21 | "babel-core": "^5.6.18",
22 | "babel-eslint": "^3.1.15",
23 | "babel-loader": "^5.1.4",
24 | "eslint": "^0.23",
25 | "eslint-config-airbnb": "0.0.6",
26 | "eslint-plugin-react": "^2.3.0",
27 | "expect": "^1.6.0",
28 | "isparta": "^3.0.3",
29 | "mocha": "^2.2.5",
30 | "rimraf": "^2.3.4",
31 | "react-hot-loader": "^1.2.7"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/lapanoid/redux-remote.git"
36 | },
37 | "author": "Sergey Lapin (http://github.com/lapanoid)",
38 | "license": "ISC",
39 | "bugs": {
40 | "url": "https://github.com/lapanoid/redux-remote/issues"
41 | },
42 | "homepage": "https://github.com/lapanoid/redux-remote#readme"
43 | }
44 |
--------------------------------------------------------------------------------
/src/createSubscribeOnStateStore.js:
--------------------------------------------------------------------------------
1 | export default (redux, SET_STATE_ACTION, reducers)=> {
2 | let passStoreUpdate = false;
3 |
4 | const onAction = (action)=> {
5 | console.log(action.type);
6 | passStoreUpdate = action.type !== SET_STATE_ACTION;
7 | };
8 |
9 | const actionCatchMiddleware = ({dispatch}) => next => action => {
10 | console.log(action);
11 | onAction(action);
12 | next(action);
13 | };
14 |
15 | const createStoreWithMiddleware = redux.applyMiddleware(actionCatchMiddleware)(redux.createStore);
16 | const store = createStoreWithMiddleware(redux.combineReducers(reducers));
17 |
18 | const subscribeOnState = (dataListener)=> {
19 | store.subscribe(() => {
20 | if (!passStoreUpdate && dataListener)
21 | return;
22 |
23 | dataListener(store.getState())
24 | });
25 | };
26 |
27 | return {
28 | dispatch: store.dispatch.bind(store),
29 | getState: store.getState.bind(store),
30 | subscribe: store.subscribe.bind(store),
31 | subscribeOnState
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/src/dataListenener.js:
--------------------------------------------------------------------------------
1 | import {diff, patch} from 'jsondiffpatch';
2 | import R from 'ramda';
3 |
4 | const differ = (initialState = {})=> {
5 | let oldState = initialState;
6 | return (data)=> {
7 | const myDiff = diff(oldState, data);
8 | oldState = data;
9 | return myDiff;
10 | }
11 | };
12 |
13 | const collecter = (initialState = {})=> {
14 | let oldState = initialState;
15 | return (diffChunk)=> {
16 | const nextState = patch(oldState, diffChunk);
17 | oldState = nextState;
18 | return nextState;
19 | }
20 | };
21 |
22 | const collecterInstFunc = collecter();
23 | const differInstFunc = differ();
24 |
25 | export const dataListener = R.curry(( dispatchState, data)=> {
26 | const clone = (d)=>JSON.parse(JSON.stringify(d));
27 | //const obj = {...b}
28 |
29 | //console.log(data == data)
30 | //console.log(data === b)
31 | //console.log(data === clone(b))
32 | console.log(data)
33 | //console.log(JSON.stringify(data))
34 | //console.log(b)
35 | //console.log(JSON.stringify(b))
36 | //console.log(JSON.stringify(clone(b)))
37 |
38 | setTimeout(()=>dispatchState(
39 | //R.compose(clone, collecterInstFunc, differInstFunc)
40 | (data)), 0)
41 | });
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as createSubscribeOnStateStore } from './createSubscribeOnStateStore';
--------------------------------------------------------------------------------