-
-
Notifications
You must be signed in to change notification settings - Fork 15.3k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"stage": 0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"ecmaFeatures": { | ||
"jsx": true, | ||
"modules": true | ||
}, | ||
"env": { | ||
"browser": true, | ||
"node": true | ||
}, | ||
"parser": "babel-eslint", | ||
"rules": { | ||
"quotes": [2, "single"], | ||
"strict": [2, "never"], | ||
"react/jsx-uses-react": 2, | ||
"react/jsx-uses-vars": 2, | ||
"react/react-in-jsx-scope": 2 | ||
}, | ||
"plugins": [ | ||
"react" | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules | ||
npm-debug.log | ||
.DS_Store |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"node": true, | ||
"browser": true, | ||
"esnext": true, | ||
"newcap": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2014 Dan Abramov | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
redux | ||
===================== | ||
|
||
Work in progress |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<html> | ||
<head> | ||
<title>Sample App</title> | ||
</head> | ||
<body> | ||
<div id='root'> | ||
</div> | ||
</body> | ||
<script src="/static/bundle.js"></script> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "redux", | ||
"version": "1.0.0", | ||
"description": "Work in progress", | ||
"scripts": { | ||
"start": "node server.js", | ||
"lint": "eslint src" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/gaearon/redux.git" | ||
}, | ||
"keywords": [ | ||
"react", | ||
"reactjs", | ||
"hot", | ||
"reload", | ||
"hmr", | ||
"live", | ||
"edit", | ||
"webpack", | ||
"flux" | ||
], | ||
"author": "Dan Abramov <dan.abramov@me.com> (http://github.com/gaearon)", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/gaearon/redux/issues" | ||
}, | ||
"homepage": "https://github.com/gaearon/redux", | ||
"devDependencies": { | ||
"babel-core": "^5.4.7", | ||
"babel-eslint": "^3.1.9", | ||
"babel-loader": "^5.1.2", | ||
"eslint-plugin-react": "^2.3.0", | ||
"react-hot-loader": "^1.2.7", | ||
"webpack": "^1.9.6", | ||
"webpack-dev-server": "^1.8.2" | ||
}, | ||
"dependencies": { | ||
"react": "^0.13.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
var webpack = require('webpack'); | ||
var WebpackDevServer = require('webpack-dev-server'); | ||
var config = require('./webpack.config'); | ||
|
||
new WebpackDevServer(webpack(config), { | ||
publicPath: config.output.publicPath, | ||
hot: true, | ||
historyApiFallback: true | ||
}).listen(3000, 'localhost', function (err, result) { | ||
if (err) { | ||
console.log(err); | ||
} | ||
|
||
console.log('Listening at localhost:3000'); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React, { Component } from 'react'; | ||
import Counter from './Counter'; | ||
import flux from './redux/flux'; | ||
import dispatcher from './dispatcher'; | ||
|
||
@flux(dispatcher) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
gaearon
Author
Contributor
|
||
export default class App extends Component { | ||
render() { | ||
return ( | ||
<Counter /> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import React from 'react'; | ||
import connect from './redux/connect'; | ||
|
||
@connect( | ||
stores => ({ | ||
counterState: stores.CounterStore | ||
}), | ||
actions => ({ | ||
increment: actions.increment | ||
}) | ||
) | ||
export default class Counter { | ||
render() { | ||
return ( | ||
<button onClick={this.props.increment}> | ||
{this.props.counterState.counter} | ||
</button> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { INCREMENT_COUNTER } from '../constants/ActionTypes'; | ||
|
||
export function increment() { | ||
return { | ||
type: INCREMENT_COUNTER | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './CounterActions'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import * as stores from './stores/index'; | ||
import * as actions from './actions/index'; | ||
import createDispatcher from './redux/createDispatcher'; | ||
|
||
const state = module.hot && module.hot.data && module.hot.data.state; | ||
const dispatcher = createDispatcher(stores, actions, state); | ||
|
||
module.hot.dispose((data) => { | ||
data.state = dispatcher.getState(); | ||
}); | ||
|
||
export default dispatcher; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import React from 'react'; | ||
import App from './App'; | ||
|
||
React.render(<App />, document.getElementById('root')); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React, { Component, PropTypes } from 'react'; | ||
|
||
const contextTypes = { | ||
observeStores: PropTypes.func.isRequired, | ||
bindActions: PropTypes.func.isRequired | ||
}; | ||
|
||
export default function connect(pickStores, pickActions) { | ||
return function (DecoratedComponent) { | ||
const wrappedDisplayName = | ||
DecoratedComponent.displayName || | ||
DecoratedComponent.name || | ||
'Component'; | ||
|
||
return class extends Component { | ||
static displayName = `ReduxConnect(${wrappedDisplayName})`; | ||
static contextTypes = contextTypes; | ||
|
||
constructor(props, context) { | ||
super(props, context); | ||
this.handleChange = this.handleChange.bind(this); | ||
|
||
this.actions = this.context.bindActions(pickActions); | ||
this.unobserve = this.context.observeStores(pickStores, this.handleChange); | ||
} | ||
|
||
handleChange(state) { | ||
if (this.state) { | ||
this.setState(state); | ||
} else { | ||
this.state = state; | ||
} | ||
} | ||
|
||
componentWillUnmount() { | ||
this.unobserve(); | ||
} | ||
|
||
render() { | ||
return ( | ||
<DecoratedComponent {...this.props} | ||
{...this.state} | ||
{...this.actions} /> | ||
); | ||
} | ||
}; | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
function mapValues(obj, fn) { | ||
const result = {}; | ||
Object.keys(obj).forEach(key => | ||
result[key] = fn(obj[key], key) | ||
); | ||
return result; | ||
} | ||
|
||
export default function createDispatcher(stores, actionCreators, initialState) { | ||
// To compute the next state, combine the next states of every store | ||
function computeState(state, action) { | ||
return mapValues(stores, | ||
(store, key) => store(state[key], action) | ||
); | ||
} | ||
|
||
// Keep all store observers by key | ||
const observers = mapValues(stores, () => []); | ||
function emitChange(changedKeys) { | ||
if (!changedKeys.length) { | ||
return; | ||
} | ||
|
||
// Gather the affected observers | ||
const notifyObservers = []; | ||
changedKeys.forEach(key => { | ||
observers[key].forEach(o => { | ||
if (notifyObservers.indexOf(o) === -1) { | ||
notifyObservers.push(o); | ||
} | ||
}); | ||
}); | ||
|
||
// Emit change | ||
notifyObservers.forEach(o => o()); | ||
} | ||
|
||
// Reassign the current state on each dispatch | ||
let currentState = initialState || computeState({}); | ||
function dispatch(action) { | ||
// Swap the state | ||
const previousState = currentState; | ||
currentState = computeState(currentState, action); | ||
|
||
// Notify the observers | ||
const changedKeys = Object.keys(currentState).filter(key => | ||
currentState[key] !== previousState[key] | ||
); | ||
emitChange(changedKeys); | ||
} | ||
|
||
// Provide subscription and unsubscription | ||
function observeStores(pickStores, onChange) { | ||
// Map store keys into the desired shape | ||
const observedKeys = pickStores( | ||
mapValues(stores, (store, key) => key) | ||
); | ||
|
||
// Emit the state update in the desired shape | ||
function handleChange() { | ||
const observedState = mapValues(observedKeys, key => currentState[key]); | ||
onChange(observedState); | ||
} | ||
|
||
// Synchronously emit the initial value | ||
handleChange(); | ||
|
||
// Register the observer for each relevant key | ||
mapValues(observedKeys, key => { | ||
observers[key].push(handleChange); | ||
}); | ||
|
||
// Let it unregister when the time comes | ||
return () => { | ||
mapValues(observedKeys, key => { | ||
const index = observers[key].indexOf(handleChange); | ||
observers[key].splice(index, 1); | ||
}); | ||
}; | ||
} | ||
|
||
// Provide dispatching | ||
function bindActions(pickActions) { | ||
return mapValues( | ||
pickActions(actionCreators), | ||
(actionCreator) => (...args) => dispatch(actionCreator(...args)) | ||
); | ||
} | ||
|
||
function getState() { | ||
return currentState; | ||
} | ||
|
||
return { | ||
bindActions, | ||
observeStores, | ||
getState | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React, { PropTypes } from 'react'; | ||
|
||
const childContextTypes = { | ||
observeStores: PropTypes.func.isRequired, | ||
bindActions: PropTypes.func.isRequired | ||
}; | ||
|
||
export default function flux(dispatcher) { | ||
const { observeStores, bindActions } = dispatcher; | ||
const childContext = { observeStores, bindActions }; | ||
|
||
return function (DecoratedComponent) { | ||
const wrappedDisplayName = | ||
DecoratedComponent.displayName || | ||
DecoratedComponent.name || | ||
'Component'; | ||
|
||
return class { | ||
static displayName = `ReduxProvide(${wrappedDisplayName})`; | ||
static childContextTypes = childContextTypes; | ||
|
||
getChildContext() { | ||
return childContext; | ||
} | ||
|
||
render() { | ||
return ( | ||
<DecoratedComponent {...this.props} /> | ||
); | ||
} | ||
}; | ||
}; | ||
} |
7 comments
on commit 8bc1465
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy belated first birthday :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy extra-belated second birthday :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy extra-extra-belated third birthday :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy extra-extra-extra-belated fourth birthday :))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy extra-extra-extra-extra-belated fifth birthday 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Continuing the trend]
Happy (extra * 6) belated seventh birthday 🎉🥳🎊🎂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy eighth birthday ^_^
I haven't found anything about the @ symbol in javascript.
@gaearon can you provide any resources or point me in the right direction on what it does?