├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── demo.sh ├── examples └── todomvc │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── actions │ └── TodoActions.js │ ├── components │ ├── Footer.js │ ├── Header.js │ ├── MainSection.js │ ├── TodoItem.js │ └── TodoTextInput.js │ ├── constants │ ├── ActionTypes.js │ ├── TodoFilters.js │ └── WireActions.js │ ├── containers │ ├── JsonApp.js │ └── TodoApp.js │ ├── dist │ └── bundle.js │ ├── index.html │ ├── index.js │ ├── package.json │ ├── reducers │ ├── index.js │ └── todos.js │ ├── server.js │ └── webpack.config.js ├── index.html ├── package.json └── src ├── createSubscribeOnStateStore.js ├── dataListenener.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0, 3 | "loose": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | **/node_modules 3 | **/webpack.config.js 4 | examples/**/server.js 5 | examples -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb", 3 | "env": { 4 | "browser": true, 5 | "mocha": true, 6 | "node": true 7 | }, 8 | "rules": { 9 | "react/jsx-uses-react": 2, 10 | "react/jsx-uses-vars": 2, 11 | "react/react-in-jsx-scope": 2, 12 | "no-console": 0, 13 | // Temporarily disabled due to babel-eslint issues: 14 | "block-scoped-var": 0, 15 | "padded-blocks": 0, 16 | }, 17 | "plugins": [ 18 | "react" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | lib 5 | coverage 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | src 4 | test 5 | examples 6 | coverage 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "iojs" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #[DEMO](http://lapanoid.github.io/redux-remote) 2 | 3 | Most importantly that I am not resend all actions but listen store updates and send it to another redux instance which updates itself. 4 | 5 | Some diff algorithm can be implemented on top of it. 6 | 7 | It can be used in webworkers or on servers to relieve client from costly computations. 8 | 9 | Syncing can be used for realtime collaboration apps. 10 | 11 | # TODO 12 | - [x] move to Redux 1.0 RC 13 | - [ ] extract code from example to src 14 | - [ ] publish npm 15 | - [ ] add doc 16 | - [ ] add demo build script 17 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit # Exit on error 4 | 5 | git stash save 6 | git checkout gh-pages 7 | git pull --rebase origin master 8 | cd examples/todomvc 9 | webpack 10 | git add . 11 | if git commit -m 'rebuild todomvc bundle for demo'; then # Commit the changes, if any 12 | echo 'Changes Committed' 13 | if git push --force; then # Commit the changes, if any 14 | echo 'Pushed succecfully' 15 | fi 16 | fi 17 | git checkout master 18 | git stash apply -------------------------------------------------------------------------------- /examples/todomvc/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /examples/todomvc/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /examples/todomvc/README.md: -------------------------------------------------------------------------------- 1 | # Redux DevTools TodoMVC example 2 | 3 | ## Getting Started 4 | 5 | 1. Install dependencies: `npm i` 6 | 2. Start the development server: `npm start` 7 | -------------------------------------------------------------------------------- /examples/todomvc/actions/TodoActions.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | export function addTodo(text) { 4 | return { 5 | type: types.ADD_TODO, 6 | text 7 | }; 8 | } 9 | 10 | export function deleteTodo(id) { 11 | return { 12 | type: types.DELETE_TODO, 13 | id 14 | }; 15 | } 16 | 17 | export function editTodo(id, text) { 18 | return { 19 | type: types.EDIT_TODO, 20 | id, 21 | text 22 | }; 23 | } 24 | 25 | export function markTodo(id) { 26 | return { 27 | type: types.MARK_TODO, 28 | id 29 | }; 30 | } 31 | 32 | export function markAll() { 33 | return { 34 | type: types.MARK_ALL 35 | }; 36 | } 37 | 38 | export function clearMarked() { 39 | return { 40 | type: types.CLEAR_MARKED 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /examples/todomvc/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import classnames from 'classnames'; 3 | import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters'; 4 | 5 | const FILTER_TITLES = { 6 | [SHOW_ALL]: 'All', 7 | [SHOW_UNMARKED]: 'Active', 8 | [SHOW_MARKED]: 'Completed' 9 | }; 10 | 11 | export default class Footer extends Component { 12 | static propTypes = { 13 | markedCount: PropTypes.number.isRequired, 14 | unmarkedCount: PropTypes.number.isRequired, 15 | filter: PropTypes.string.isRequired, 16 | onClearMarked: PropTypes.func.isRequired, 17 | onShow: PropTypes.func.isRequired 18 | } 19 | 20 | render() { 21 | return ( 22 | 33 | ); 34 | } 35 | 36 | renderTodoCount() { 37 | const { unmarkedCount } = this.props; 38 | const itemWord = unmarkedCount === 1 ? 'item' : 'items'; 39 | 40 | return ( 41 | 42 | {unmarkedCount || 'No'} {itemWord} left 43 | 44 | ); 45 | } 46 | 47 | renderFilterLink(filter) { 48 | const title = FILTER_TITLES[filter]; 49 | const { filter: selectedFilter, onShow } = this.props; 50 | 51 | return ( 52 | onShow(filter)}> 55 | {title} 56 | 57 | ); 58 | } 59 | 60 | renderClearButton() { 61 | const { markedCount, onClearMarked } = this.props; 62 | if (markedCount > 0) { 63 | return ( 64 | 68 | ); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/todomvc/components/Header.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react'; 2 | import TodoTextInput from './TodoTextInput'; 3 | 4 | export default class Header extends Component { 5 | static propTypes = { 6 | addTodo: PropTypes.func.isRequired 7 | }; 8 | 9 | handleSave(text) { 10 | if (text.length !== 0) { 11 | this.props.addTodo(text); 12 | } 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |

todos

19 | 22 |
23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/todomvc/components/MainSection.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import TodoItem from './TodoItem'; 3 | import Footer from './Footer'; 4 | import { SHOW_ALL, SHOW_MARKED, SHOW_UNMARKED } from '../constants/TodoFilters'; 5 | 6 | const TODO_FILTERS = { 7 | [SHOW_ALL]: () => true, 8 | [SHOW_UNMARKED]: todo => !todo.marked, 9 | [SHOW_MARKED]: todo => todo.marked 10 | }; 11 | 12 | export default class MainSection extends Component { 13 | static propTypes = { 14 | todos: PropTypes.array.isRequired, 15 | actions: PropTypes.object.isRequired 16 | }; 17 | 18 | constructor(props, context) { 19 | super(props, context); 20 | this.state = { filter: SHOW_ALL }; 21 | } 22 | 23 | handleClearMarked() { 24 | const atLeastOneMarked = this.props.todos.some(todo => todo.marked); 25 | if (atLeastOneMarked) { 26 | this.props.actions.clearMarked(); 27 | } 28 | } 29 | 30 | handleShow(filter) { 31 | this.setState({ filter }); 32 | } 33 | 34 | render() { 35 | const { todos, actions } = this.props; 36 | const { filter } = this.state; 37 | 38 | const filteredTodos = todos.filter(TODO_FILTERS[filter]); 39 | const markedCount = todos.reduce((count, todo) => 40 | todo.marked ? count + 1 : count, 41 | 0 42 | ); 43 | 44 | return ( 45 |
46 | {this.renderToggleAll(markedCount)} 47 | 52 | {this.renderFooter(markedCount)} 53 |
54 | ); 55 | } 56 | 57 | renderToggleAll(markedCount) { 58 | const { todos, actions } = this.props; 59 | if (todos.length > 0) { 60 | return ( 61 | 65 | ); 66 | } 67 | } 68 | 69 | renderFooter(markedCount) { 70 | const { todos } = this.props; 71 | const { filter } = this.state; 72 | const unmarkedCount = todos.length - markedCount; 73 | 74 | if (todos.length) { 75 | return ( 76 |