Comparing version 0.6.0 to 0.7.0
57
API.md
@@ -360,9 +360,7 @@ # API | ||
return { | ||
value: [], | ||
deps: ['todos'], | ||
get(cerebral, deps, refs) { | ||
initialState: [], | ||
lookupState: ['todos'], | ||
get(cerebral, lookupState, refs) { | ||
return refs.map(function (ref) { | ||
return deps.todos.filter(function (ref) { | ||
return todo.ref === ref; | ||
}).pop(); | ||
return lookupState.todos[ref]; | ||
}); | ||
@@ -374,3 +372,3 @@ } | ||
let cerebral = Cerebral({ | ||
todos: [], | ||
todos: {}, | ||
visibleTodos: visibleTodos, | ||
@@ -383,3 +381,3 @@ count: 0, | ||
``` | ||
In this example we have an array of `visibleTodos`. This array will contain references to todos in the `todos` array. Whenever the changes are done to either arrays the callback will run and any components using the state will update with the new value. | ||
In this example we have an array of `visibleTodos`. This array will contain references to todos in the `todos` map. See video []() on how to structure state. Whenever the changes are done to either the array or the map, the callback will run and any components using the state will update with the new value. | ||
@@ -515,3 +513,4 @@ ### Mutate state | ||
**So why use this?**. When you download and update data in the client you should put that data in a map (object) and use arrays to display that data. The reason is that these gives you a data storage of sorts. Displaying data will be used by referencing this source data. To give complete control of all this referencing you can use cerebrals implementation. | ||
##### So why use this? | ||
When you download and update data in the client you should put that data in a map (object) and use arrays to display that data. The reason is that these gives you a data storage of sorts. Displaying data will be used by referencing this source data. To give complete control of all this referencing you can use Cerebrals implementation. | ||
@@ -540,4 +539,6 @@ Let us optimistically update our application with a new todo. | ||
.success(function (id) { | ||
cerebral.ref.update(todo.ref, id); | ||
return id; | ||
return { | ||
ref: ref, | ||
id: id | ||
}; | ||
}); | ||
@@ -549,12 +550,14 @@ }; | ||
```js | ||
let setTodoId = function (cerebral, id) { | ||
let ref = cerebral.ref.get(id); | ||
let todo = cerebral.get(['todos', ref]); | ||
let setTodoId = function (cerebral, result) { | ||
cerebral.ref.update(result.ref, result.id); | ||
let todo = cerebral.get(['todos', result.ref]); | ||
cerebral.set([todo, 'id'], id); | ||
}; | ||
``` | ||
We can now use the id to get our ref. The ref is used to grab the todo and we set the id to it. | ||
We can now update the ref with the id. This allows us to do `cerebral.ref.get(todo.id)` and it returns the ref which can be used to `cerebral.get('todos', ref)`. | ||
What we have achieved with this implementation is to remove the need for ids completely. The client, with cerebral, is in full control of all objects and you can use both refs and ids to find what you need. This implementation becomes especially useful when mapping relational data. | ||
What we have achieved with this implementation is to remove the need for ids completely. The client, with Cerebral, is in full control of all objects and you can use both refs and ids to find what you need. This implementation becomes especially useful when mapping relational data. | ||
An example of this would be in a mpping function to grab a user from a usersmap using the referenced `authorId`. | ||
```js | ||
@@ -566,19 +569,13 @@ let cerebral = Cerebral({ | ||
return { | ||
value: [], | ||
deps: ['projects', 'users'], | ||
get(cerebral, deps, projectRefs) { | ||
initialState: [], | ||
lookupState: ['projects', 'users'], | ||
get(cerebral, lookupState, projectRefs) { | ||
return projectRefs.map(function (ref) { | ||
let project = deps.projects[ref].toJS(); | ||
let userRef = cerebral.ref.get(project.authorId); | ||
let project = lookupState.projects[ref].toJS(); | ||
let userRef = lookupState.users[cerebral.ref.get(project.authorId)] || { | ||
$notFound: true | ||
}; | ||
if (userRef) { | ||
project.author = deps.users[userRef]; | ||
} else { | ||
project.author = { | ||
$notFound: true | ||
}; | ||
} | ||
return project; | ||
@@ -603,2 +600,4 @@ | ||
copy.title; // "bar" | ||
cerebral.toJS(); // The whole cerebral | ||
``` | ||
@@ -605,0 +604,0 @@ |
@@ -18,3 +18,3 @@ var path = require('path'); | ||
test: /\.js$/, | ||
loader: 'babel?optional=es7.classProperties', | ||
loader: 'babel?optional=es7.decorators', | ||
exclude: node_modules | ||
@@ -21,0 +21,0 @@ }] |
@@ -18,3 +18,3 @@ var path = require('path'); | ||
test: /\.js$/, | ||
loader: 'babel?optional=es7.classProperties', | ||
loader: 'babel?optional=es7.decorators', | ||
exclude: node_modules | ||
@@ -21,0 +21,0 @@ }] |
@@ -5,12 +5,10 @@ import React from 'react'; | ||
import TodosFooter from './components/TodosFooter.js'; | ||
import mixin from './../src/mixin.js'; | ||
import Cerebral from './../src/decorator.js'; | ||
var App = React.createClass({ | ||
getCerebralState: function () { | ||
return ['visibleTodos', 'todos', 'foo']; | ||
}, | ||
render: function() { | ||
@Cerebral(['visibleTodos', 'todos', 'foo']) | ||
class App extends React.Component { | ||
render() { | ||
return ( | ||
<div id="todoapp-wrapper"> | ||
<div>{this.state.foo}</div> | ||
<div>{this.props.foo}</div> | ||
<section id="todoapp"> | ||
@@ -22,4 +20,4 @@ <header id="header"> | ||
{this.state.visibleTodos.length ? <TodosList/> : null} | ||
{Object.keys(this.state.todos).length ? <TodosFooter/> : null} | ||
{this.props.visibleTodos.length ? <TodosList/> : null} | ||
{Object.keys(this.props.todos).length ? <TodosFooter/> : null} | ||
</section> | ||
@@ -36,4 +34,4 @@ <footer id="info"> | ||
} | ||
}); | ||
} | ||
module.exports = mixin(App); | ||
module.exports = App; |
@@ -7,7 +7,7 @@ import Cerebral from './../src/Cerebral.js'; | ||
return { | ||
value: [], | ||
deps: ['todos'], | ||
get: function(cerebral, deps, refs) { | ||
initialState: [], | ||
lookupState: ['todos'], | ||
get: function(cerebral, sourceState, refs) { | ||
return refs.map(function(ref) { | ||
return deps.todos[ref]; | ||
return sourceState.todos[ref]; | ||
}); | ||
@@ -14,0 +14,0 @@ } |
import React from 'react'; | ||
import mixin from './../../src/mixin.js'; | ||
import State from './../../src/decorator.js'; | ||
class AddTodo extends React.Component { | ||
getCerebralState() { | ||
return [ | ||
'isSaving', | ||
'newTodoTitle' | ||
]; | ||
} | ||
addTodo(event) { | ||
@@ -29,4 +21,4 @@ event.preventDefault(); | ||
placeholder="What needs to be done?" | ||
disabled={this.state.isSaving} | ||
value={this.state.newTodoTitle} | ||
disabled={this.props.isSaving} | ||
value={this.props.newTodoTitle} | ||
onChange={this.setNewTodoTitle.bind(this)} | ||
@@ -40,2 +32,2 @@ /> | ||
export default mixin(AddTodo); | ||
export default State(AddTodo, ['isSaving', 'newTodoTitle']); |
import React from 'react'; | ||
import classNames from 'classnames'; | ||
import mixin from './../../src/mixin.js'; | ||
import Cerebral from './../../src/decorator.js'; | ||
@Cerebral() | ||
class Todo extends React.Component { | ||
@@ -20,3 +21,3 @@ | ||
input.value = input.value; | ||
}, 0); | ||
}, 0); | ||
} | ||
@@ -31,3 +32,3 @@ | ||
actions.saveEdit(this.props.todo.id, this.state.newTitle); | ||
} | ||
} | ||
@@ -89,2 +90,2 @@ onNewTitleSubmitted(event) { | ||
export default mixin(Todo); | ||
export default Todo; |
import React from 'react/addons'; | ||
import mixin from './../../src/mixin.js'; | ||
import Cerebral from './../../src/decorator.js'; | ||
class TodosFooter extends React.Component { | ||
getCerebralState() { | ||
return [ | ||
'remainingCount', | ||
'completedCount', | ||
'filter' | ||
]; | ||
} | ||
renderRemainingCount() { | ||
let count = this.state.remainingCount; | ||
let count = this.props.remainingCount; | ||
if (count === 0 || count > 1) { | ||
@@ -24,3 +15,3 @@ return count + ' items left'; | ||
renderRouteClass(filter) { | ||
return this.state.filter === filter ? 'selected' : ''; | ||
return this.props.filter === filter ? 'selected' : ''; | ||
} | ||
@@ -31,3 +22,3 @@ | ||
<button id="clear-completed" onClick={this.signals.clearCompletedClicked}> | ||
Clear completed ({this.state.completedCount}) | ||
Clear completed ({this.props.completedCount}) | ||
</button> | ||
@@ -52,3 +43,3 @@ ); | ||
</ul> | ||
{this.state.completedCount ? this.renderCompletedButton() : null} | ||
{this.props.completedCount ? this.renderCompletedButton() : null} | ||
</footer> | ||
@@ -60,2 +51,2 @@ ); | ||
export default mixin(TodosFooter); | ||
export default Cerebral(TodosFooter, ['remainingCount', 'completedCount', 'filter']); |
import React from 'react/addons'; | ||
import Todo from './Todo.js'; | ||
import mixin from './../../src/mixin.js'; | ||
import Cerebral from './../../src/decorator.js'; | ||
@Cerebral(['isAllChecked', 'visibleTodos']) | ||
class TodosList extends React.Component { | ||
getCerebralState() { | ||
return [ | ||
'isAllChecked', | ||
'visibleTodos' | ||
]; | ||
} | ||
renderTodo(todo, index) { | ||
var state = this.props.state; | ||
return <Todo key={index} index={index} todo={todo}/> | ||
@@ -26,3 +18,3 @@ } | ||
type="checkbox" | ||
checked={this.state.isAllChecked} | ||
checked={this.props.isAllChecked} | ||
onChange={this.signals.toggleAllChanged} | ||
@@ -32,3 +24,3 @@ /> | ||
<ul id="todo-list"> | ||
{this.state.visibleTodos.map(this.renderTodo.bind(this))} | ||
{this.props.visibleTodos.map(this.renderTodo.bind(this))} | ||
</ul> | ||
@@ -41,2 +33,2 @@ </section> | ||
export default mixin(TodosList); | ||
export default TodosList; |
@@ -44,3 +44,2 @@ import './../node_modules/todomvc-common/base.css'; | ||
console.log('location.pathname', location.pathname.substr(0, location.pathname.length - 1)); | ||
Page.base(location.pathname.substr(0, location.pathname.length - 1)); | ||
@@ -47,0 +46,0 @@ |
{ | ||
"name": "cerebral", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "An intelligent react application framework", | ||
@@ -8,3 +8,3 @@ "main": "src/Cerebral.js", | ||
"demo": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --progress --colors --content-base demo --config demo.config.js", | ||
"deploydemo": "./node_modules/webpack/bin/webpack.js --config demo.production.config.js", | ||
"deploydemo": "./node_modules/webpack/bin/webpack.js --p --config demo.production.config.js", | ||
"deploy": "NODE_ENV=production ./node_modules/webpack/bin/webpack.js -p", | ||
@@ -36,4 +36,4 @@ "start": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --devtool eval --progress --colors --content-base build", | ||
"peerDependencies": { | ||
"react": "^0.13.2" | ||
"react": "^0.13.3" | ||
} | ||
} |
@@ -14,6 +14,11 @@ # cerebral - alpha ![build status](https://travis-ci.org/christianalfoni/cerebral.svg?branch=master) | ||
- **[Get started - 4:13](http://www.youtube.com/watch?v=Mm4B5F432SQ)**. See how you get started with your Cerebral app. Using a boilerplate you have everything you need for a great workflow and even a server you can put into production. | ||
- **[The debugger - 8:13](http://www.youtube.com/watch?v=Fo86aiBoomE)**. We take a look at the powerful debugger for Cerebral. Giving you complete overview and control of your application state flow. | ||
- **[Building your first app - 14:24](https://www.youtube.com/watch?v=ZG1omJek6SY)**. We build the hello world application in the boilerplate using our debugger to explain a pretty awesome workflow. | ||
- **[Complex relational state - 37:17](http://www.youtube.com/watch?v=xx7Y2MkYgUA)**. We are going to implement some of the more complex state handling. Relational data with two different endpoints and caching. | ||
- **[Creating dynamic state - 5:32](https://www.youtube.com/watch?v=Dnz0HY35tf8)**. We are going to look at where and how you create dynamic state. | ||
- **[Caching and relational state - 28:40](https://www.youtube.com/watch?v=6W0S2p01Paw)**. We are going to handle some really complex stuff. A table with pagination and caching in place, but also automatically request users from the servers as we load projects. | ||
## Cerebral - The abstraction | ||
@@ -20,0 +25,0 @@ Read this article introducing Cerebral: [Cerebral developer preview](http://christianalfoni.com/articles/2015_05_18_Cerebral-developer-preview) |
@@ -6,7 +6,4 @@ 'use strict'; | ||
- [OPTIMIZE] If setting the same value, avoid doing extra work | ||
- Freeze data returned from maps? Create a store maybe? | ||
- Comment all code | ||
- Do not record when in production | ||
- More tests! reset, async travelling... | ||
- Fix bug where toggling record/not record | ||
- Display error in debugger when promise fails | ||
@@ -69,3 +66,3 @@ - How to trigger signals correctly as async singals are running? | ||
map(path, description); | ||
return description.value; | ||
return description.initialState; | ||
}; | ||
@@ -164,5 +161,16 @@ | ||
// Toggle if the EventStore should store state | ||
// in local storage | ||
cerebral.toggleStoreState = function() { | ||
helpers.eventStore.toggleStoreState(); | ||
}; | ||
// Check if the EventStore will store its state in localStorage | ||
cerebral.willStoreState = function() { | ||
return helpers.eventStore.willStoreState; | ||
}; | ||
// Extracts all the state of the application. Used to put into | ||
// localStorage | ||
cerebral.extractState = function() { | ||
cerebral.toJS = function() { | ||
return helpers.currentState.toJS(); | ||
@@ -193,5 +201,5 @@ }; | ||
var mapValue = utils.getMapPath(path, maps); | ||
if (mapValue) { | ||
return mapValue[0]; | ||
var mapGetter = utils.getMapPath(path, maps); | ||
if (mapGetter) { | ||
return mapGetter.value; | ||
} | ||
@@ -210,2 +218,3 @@ | ||
window.addEventListener('beforeunload', function() { | ||
if (!utils.hasLocalStorage()) { | ||
@@ -215,3 +224,3 @@ return; | ||
if (helpers.eventStore.willKeepState) { | ||
if (helpers.eventStore.willStoreState) { | ||
localStorage.setItem('cerebral_state', JSON.stringify(helpers.eventStore.initialState)); | ||
@@ -225,3 +234,6 @@ localStorage.setItem('cerebral_signals', JSON.stringify(helpers.eventStore.signals)); | ||
} | ||
localStorage.setItem('cerebral_keepState', helpers.eventStore.willKeepState.toString()); | ||
localStorage.setItem('cerebral_storeState', helpers.eventStore.willStoreState.toString()); | ||
}); | ||
@@ -236,3 +248,3 @@ | ||
}); | ||
return cerebral; | ||
@@ -239,0 +251,0 @@ |
@@ -9,13 +9,23 @@ "use strict"; | ||
path = (typeof path === 'string' ? [path] : path).slice(); | ||
description.lookupState = description.lookupState || []; | ||
if (!('initialState' in description) || | ||
!('lookupState' in description) || | ||
!('get' in description) | ||
) { | ||
throw new Error('Cerebral - You have to pass "initialState", "lookupState" and "get" properties to the mapping description'); | ||
} | ||
var prevResult = null; | ||
var state = null; | ||
var callback = description.get; | ||
var values = []; | ||
// Need something to hold the current value | ||
var getter = {value: null}; | ||
// Convert deps to key/value to expose state | ||
var depPathsObject = utils.pathsToObject(description.deps); | ||
var depPathsObject = utils.pathsToObject(description.lookupState); | ||
// Convert deps + state path to array to use for state retrieval and equality check | ||
var allPaths = utils.objectToPaths(description.deps).concat(path); | ||
var allPaths = utils.objectToPaths(description.lookupState).concat(path); | ||
@@ -57,11 +67,3 @@ // Get all depending state values | ||
var setValue = function(value) { | ||
values.unshift(value); | ||
// When remembering subsignals that are async we need to reverse the values | ||
// as the async value should be picked instea | ||
if (!!helpers.subSignal && store.isRemembering) { | ||
values.reverse(); | ||
} | ||
getter.value = value; | ||
}; | ||
@@ -72,6 +74,6 @@ | ||
while (pathCopy.length) { | ||
mapPath = mapPath[pathCopy.shift()] = pathCopy.length ? {} : values; | ||
mapPath = mapPath[pathCopy.shift()] = pathCopy.length ? {} : getter; | ||
} | ||
setValue(description.value); | ||
setValue(description.initialState); | ||
@@ -78,0 +80,0 @@ store.on('mapUpdate', update); |
@@ -11,3 +11,3 @@ /* | ||
if (arguments.length) { | ||
if (helpers.ids.indexOf(id) >= 0) { | ||
if (id && helpers.ids.indexOf(id) >= 0) { | ||
return helpers.refs[helpers.ids.indexOf(id)]; | ||
@@ -14,0 +14,0 @@ } |
@@ -11,9 +11,8 @@ "use strict"; | ||
store.signals[name] = function() { | ||
var args = [].slice.call(arguments); | ||
var executionArray = callbacks.slice(); | ||
var signalIndex = helpers.eventStore.willKeepState ? | ||
!!helpers.runningSignal ? helpers.eventStore.currentIndex : ++helpers.eventStore.currentIndex : 0; | ||
var initiatedSignal = helpers.runningSignal || name; | ||
var signalIndex = helpers.eventStore.willKeepState ? ++helpers.eventStore.currentIndex : 0; | ||
var runMutation = function() { | ||
var runSignal = function() { | ||
@@ -104,5 +103,5 @@ var timestamp = Date.now(); | ||
helpers.asyncCallbacks[initiatedSignal] = helpers.asyncCallbacks[initiatedSignal] || {}; | ||
helpers.asyncCallbacks[initiatedSignal][signalIndex] = helpers.asyncCallbacks[initiatedSignal][signalIndex] || {}; | ||
helpers.asyncCallbacks[initiatedSignal][signalIndex][action.name] = result || null; // JS or Chrome bug causing undefined not to set key | ||
helpers.asyncCallbacks[name] = helpers.asyncCallbacks[name] || {}; | ||
helpers.asyncCallbacks[name][signalIndex] = helpers.asyncCallbacks[name][signalIndex] || {}; | ||
helpers.asyncCallbacks[name][signalIndex][action.name] = result || null; // JS or Chrome bug causing undefined not to set key | ||
@@ -119,2 +118,13 @@ helpers.eventStore.addAsyncSignal({ | ||
}).catch(function(err) { | ||
if ( | ||
err instanceof EvalError || | ||
err instanceof RangeError || | ||
err instanceof ReferenceError || | ||
err instanceof SyntaxError || | ||
err instanceof TypeError | ||
) { | ||
throw err; | ||
} | ||
helpers.eventStore.addAsyncSignal({ | ||
@@ -141,5 +151,3 @@ signalIndex: signalIndex, | ||
if (!helpers.runningSignal) { | ||
helpers.eventStore.addSignal(signal); | ||
} | ||
helpers.eventStore.addSignal(signal); | ||
execute.apply(null, args); | ||
@@ -150,5 +158,5 @@ | ||
if (!!helpers.runningSignal || store.isRemembering || typeof requestAnimationFrame === 'undefined') { | ||
runMutation(); | ||
runSignal(); | ||
} else { | ||
requestAnimationFrame(runMutation); | ||
requestAnimationFrame(runSignal); | ||
} | ||
@@ -155,0 +163,0 @@ |
@@ -30,6 +30,7 @@ var React = require('react'); | ||
verticalAlign: 'top', | ||
width: '30px', | ||
padding: '3px 5px', | ||
marginRight: '10px', | ||
height: '30px', | ||
backgroundColor: '#222', | ||
lineHeight: '25px', | ||
lineHeight: '30px', | ||
textAlign: 'center', | ||
@@ -41,2 +42,16 @@ cursor: 'pointer', | ||
var StoreStateButton = { | ||
display: 'inline-block', | ||
verticalAlign: 'top', | ||
padding: '3px 5px', | ||
marginRight: '10px', | ||
height: '30px', | ||
backgroundColor: '#222', | ||
lineHeight: '30px', | ||
textAlign: 'center', | ||
cursor: 'pointer', | ||
border: '1px solid #444', | ||
borderRadius: '3px' | ||
}; | ||
var MutationsStyle = { | ||
@@ -95,3 +110,3 @@ listStyleType: 'none', | ||
}, | ||
logArg: function (arg) { | ||
logArg: function(arg) { | ||
console.log(arg); | ||
@@ -140,6 +155,8 @@ }, | ||
), | ||
DOM.small({ | ||
style: {color: '#888'} | ||
}, action.signalName !== signal.name ? ' (' + action.signalName + ')' : null) | ||
), | ||
DOM.small({ | ||
style: { | ||
color: '#888' | ||
} | ||
}, action.signalName !== signal.name ? ' (' + action.signalName + ')' : null) | ||
), | ||
DOM.ul({ | ||
@@ -177,3 +194,5 @@ style: ActionStyle | ||
key: index, | ||
style: {cursor: 'pointer'}, | ||
style: { | ||
cursor: 'pointer' | ||
}, | ||
onClick: this.logArg.bind(null, mutationArg) | ||
@@ -210,3 +229,3 @@ }, argString.substr(0, 50) + '...'); | ||
}, | ||
reset: function () { | ||
reset: function() { | ||
this.context.cerebral.reset(); | ||
@@ -222,2 +241,3 @@ }, | ||
var keepState = cerebral.willKeepState(); | ||
var storeState = cerebral.willStoreState(); | ||
var lockInput = cerebral.hasExecutingAsyncSignals() || !keepState; | ||
@@ -228,2 +248,5 @@ | ||
StoreStateButton.backgroundColor = storeState ? '#5cb85c' : '#222'; | ||
StoreStateButton.color = storeState ? 'white' : '#666'; | ||
return DOM.div({ | ||
@@ -233,15 +256,26 @@ style: debuggerStyle | ||
DOM.h1({ | ||
style: { | ||
lineHeight: '30px', | ||
fontSize: '2em' | ||
} | ||
}, 'Cerebral Debugger ', DOM.span({ | ||
onClick: cerebral.toggleKeepState, | ||
style: LockStateButton | ||
}, '⦿')), | ||
style: { | ||
lineHeight: '30px', | ||
fontSize: '2em' | ||
} | ||
}, | ||
'Cerebral Debugger '), | ||
DOM.h4(null, | ||
DOM.span(null, value + ' / ' + steps + ' - ', DOM.a({ | ||
DOM.span({ | ||
onClick: cerebral.toggleKeepState, | ||
style: LockStateButton | ||
}, 'record'), | ||
DOM.span({ | ||
onClick: cerebral.toggleStoreState, | ||
style: StoreStateButton | ||
}, 'store'), | ||
DOM.span({ | ||
style: { | ||
height: '38px', | ||
lineHeight: '38px' | ||
} | ||
}, value + ' / ' + steps + ' - ', DOM.a({ | ||
onClick: this.reset, | ||
style: { | ||
textDecoration: cerebral.hasExecutingAsyncSignals() ? 'none' :'underline', | ||
textDecoration: cerebral.hasExecutingAsyncSignals() ? 'none' : 'underline', | ||
cursor: cerebral.hasExecutingAsyncSignals() ? 'default' : 'pointer', | ||
@@ -248,0 +282,0 @@ color: cerebral.hasExecutingAsyncSignals() ? '#444' : '#888' |
@@ -22,5 +22,20 @@ "use strict"; | ||
// Indicates if signals should be stored or replaced. Grabs from localStorage if available | ||
this.willKeepState = utils.hasLocalStorage() && localStorage.getItem('cerebral_keepState') ? | ||
JSON.parse(localStorage.getItem('cerebral_keepState')) : true; | ||
this.willKeepState = ( | ||
process.env.NODE_ENV === 'production' ? | ||
false : | ||
utils.hasLocalStorage() && localStorage.getItem('cerebral_keepState') ? | ||
JSON.parse(localStorage.getItem('cerebral_keepState')) : | ||
true | ||
); | ||
// Indicates if state should be stored into localStorage | ||
this.willStoreState = ( | ||
process.env.NODE_ENV === 'production' ? | ||
false : | ||
utils.hasLocalStorage() && localStorage.getItem('cerebral_storeState') ? | ||
JSON.parse(localStorage.getItem('cerebral_storeState')) : | ||
true | ||
); | ||
this.signals = signals; | ||
@@ -55,2 +70,8 @@ this.asyncSignals = []; | ||
// Flips flag of storing state into localStorage | ||
EventStore.prototype.toggleStoreState = function() { | ||
this.willStoreState = !this.willStoreState; | ||
this.cerebral.emit('eventStoreUpdate'); | ||
}; | ||
EventStore.prototype.addAsyncSignal = function(signal) { | ||
@@ -67,3 +88,3 @@ | ||
// Or add it to the list | ||
// Or add it to the list | ||
} else { | ||
@@ -96,3 +117,3 @@ | ||
} | ||
// If we are not keeping the state around reset the signals to just | ||
@@ -110,5 +131,5 @@ // keep the latest one | ||
signal.index = this.signals.length; | ||
// Add signal and set the current signal to be the recently added signal | ||
this.signals.push(signal); | ||
this.currentIndex = this.signals.length - 1; | ||
@@ -146,12 +167,2 @@ }; | ||
/* | ||
TODO: Is this needed? As it only runs when starting app. Maybe when using react-hot-loader? | ||
if (this.hasExecutingAsyncSignals) { | ||
var lastAsyncSignal = this.asyncSignals.sort(function(a, b) { | ||
return a.signalIndex - b.signalIndex; | ||
}) | ||
.pop(); | ||
this.signals.splice(lastAsyncSignal.signalIndex, this.signals.length - lastAsyncSignal.signalIndex); | ||
} | ||
*/ | ||
this.travel(this.signals.length - 1, state); | ||
@@ -174,3 +185,3 @@ this.isSilent = false; | ||
// new props | ||
Object.keys(state).forEach(function (key) { | ||
Object.keys(state).forEach(function(key) { | ||
state = state.unset(key); | ||
@@ -177,0 +188,0 @@ }); |
"use strict"; | ||
var React = require('react'); | ||
var React = require('react'); | ||
var utils = require('./utils.js'); | ||
var contextTypes = { | ||
cerebral: React.PropTypes.object | ||
}; | ||
var mixin = { | ||
contextTypes: { | ||
cerebral: React.PropTypes.object | ||
}, | ||
componentWillMount: function() { | ||
this._mergeInState = this._mergeInState; | ||
this.signals = this.context.cerebral.signals; | ||
var componentWillMount = function() { | ||
this._mergeInState = this._mergeInState.bind(this); | ||
this.signals = this.context.cerebral.signals; | ||
if (this.getCerebralState) { | ||
this.context.cerebral.on('update', this._mergeInState); | ||
this._mergeInState(); | ||
} | ||
}, | ||
shouldComponentUpdate: function(nextProps, nextState) { | ||
return !utils.shallowEqual(this.props, nextProps) || | ||
!utils.shallowEqual(this.state, nextState); | ||
}, | ||
componentWillUnmount: function() { | ||
this.isUnMounting = true; // removeListener is async? | ||
this.context.cerebral.removeListener('update', this._mergeInState); | ||
}, | ||
_mergeInState: function() { | ||
if (this.isUnMounting) { | ||
return true; | ||
} | ||
var statePaths = this.getCerebralState ? this.getCerebralState() : []; | ||
var cerebral = this.context.cerebral; | ||
if (this.getCerebralState) { | ||
this.context.cerebral.on('update', this._mergeInState); | ||
this._mergeInState(); | ||
} | ||
}; | ||
if (Array.isArray(statePaths)) { | ||
var shouldComponentUpdate = function(nextProps, nextState) { | ||
return !utils.shallowEqual(this.props, nextProps) || | ||
!utils.shallowEqual(this.state, nextState); | ||
}; | ||
this.setState(statePaths.reduce(function(state, key, index) { | ||
state[key] = cerebral.get(statePaths[index]); | ||
return state; | ||
}, {})); | ||
var componentWillUnmount = function() { | ||
this.isUnMounting = true; // removeListener is async? | ||
this.context.cerebral.removeListener('update', this._mergeInState); | ||
}; | ||
} else { | ||
var _mergeInState = function() { | ||
if (this.isUnMounting) { | ||
return true; | ||
this.setState(Object.keys(statePaths).reduce(function(state, key) { | ||
state[key] = cerebral.get(statePaths[key]); | ||
return state; | ||
}, {})); | ||
} | ||
} | ||
var statePaths = this.getCerebralState ? this.getCerebralState() : []; | ||
var cerebral = this.context.cerebral; | ||
if (Array.isArray(statePaths)) { | ||
this.setState(statePaths.reduce(function(state, key, index) { | ||
state[key] = cerebral.get(statePaths[index]); | ||
return state; | ||
}, {})); | ||
} else { | ||
this.setState(Object.keys(statePaths).reduce(function(state, key) { | ||
state[key] = cerebral.get(statePaths[key]); | ||
return state; | ||
}, {})); | ||
} | ||
}; | ||
var mixin = function(component) { | ||
component.contextTypes = contextTypes; | ||
component.prototype.componentWillMount = componentWillMount; | ||
component.prototype.shouldComponentUpdate = shouldComponentUpdate; | ||
component.prototype.componentWillUnmount = componentWillUnmount; | ||
component.prototype._mergeInState = _mergeInState; | ||
return component; | ||
}; | ||
mixin.contextTypes = contextTypes; | ||
mixin.componentWillMount = componentWillMount; | ||
mixin.shouldComponentUpdate = shouldComponentUpdate; | ||
mixin.componentWillUnmount = componentWillUnmount; | ||
mixin._mergeInState = _mergeInState; | ||
module.exports = mixin; |
@@ -137,7 +137,7 @@ "use strict"; | ||
// Converts an array of paths to key/value | ||
pathsToObject: function (obj) { | ||
pathsToObject: function(obj) { | ||
if (!Array.isArray(obj)) { | ||
return obj; | ||
} | ||
return obj.reduce(function (obj, value) { | ||
return obj.reduce(function(obj, value) { | ||
obj[typeof value === 'string' ? value : value.slice().pop()] = value; | ||
@@ -149,9 +149,19 @@ return obj; | ||
// Converts an object to array of paths | ||
objectToPaths: function (obj) { | ||
objectToPaths: function(obj) { | ||
if (Array.isArray(obj)) { | ||
return obj; | ||
} | ||
return Object.keys(obj).map(function (key) { | ||
return Object.keys(obj).map(function(key) { | ||
return obj[key]; | ||
}); | ||
}, | ||
extend: function() { | ||
var objects = [].slice.call(arguments); | ||
var initialObject = objects.shift(); | ||
return objects.reduce(function(returnedObject, object) { | ||
return Object.keys(object).reduce(function(returnedObject, key) { | ||
returnedObject[key] = object[key]; | ||
return returnedObject; | ||
}, returnedObject); | ||
}, initialObject); | ||
} | ||
@@ -158,0 +168,0 @@ }; |
@@ -30,2 +30,15 @@ var Cerebral = require('./../src/Cerebral.js'); | ||
cerebral.signals.test(); | ||
}; | ||
exports['should be able to export all state to plain JS'] = function(test) { | ||
var cerebral = new Cerebral({ | ||
list: [{ | ||
title: 'foo' | ||
}] | ||
}); | ||
var js = cerebral.toJS(); | ||
test.deepEqual(js, {list: [{title: 'foo'}]}); | ||
js.list = []; | ||
test.deepEqual(js, {list: []}); | ||
test.done(); | ||
}; |
var Cerebral = require('./../src/Cerebral.js'); | ||
exports['should run maps on instantiation'] = function(test) { | ||
@@ -7,6 +8,6 @@ var cerebral = new Cerebral({ | ||
return { | ||
value: [], | ||
deps: ['bar'], | ||
get: function (cerebral, deps, array) { | ||
return deps.bar; | ||
initialState: [], | ||
lookupState: ['bar'], | ||
get: function (cerebral, lookupState, array) { | ||
return lookupState.bar; | ||
} | ||
@@ -25,8 +26,8 @@ }; | ||
return { | ||
value: [], | ||
deps: { | ||
initialState: [], | ||
lookupState: { | ||
foo: 'bar' | ||
}, | ||
get: function (cerebral, deps, array) { | ||
return deps.foo; | ||
get: function (cerebral, lookupState, array) { | ||
return lookupState.foo; | ||
} | ||
@@ -45,6 +46,6 @@ }; | ||
return { | ||
value: [], | ||
deps: [['bar', 'foo']], | ||
get: function (cerebral, deps, array) { | ||
return deps.foo; | ||
initialState: [], | ||
lookupState: [['bar', 'foo']], | ||
get: function (cerebral, lookupState, array) { | ||
return lookupState.foo; | ||
} | ||
@@ -65,6 +66,6 @@ }; | ||
return { | ||
value: [], | ||
deps: ['bar'], | ||
get: function (cerebral, deps, array) { | ||
return array.length | ||
initialState: [], | ||
lookupState: ['bar'], | ||
get: function (cerebral, lookupState, array) { | ||
return array.length; | ||
} | ||
@@ -84,10 +85,10 @@ }; | ||
exports['should update when deps changes'] = function(test) { | ||
exports['should update when lookupState changes'] = function(test) { | ||
var cerebral = new Cerebral({ | ||
foo: function () { | ||
return { | ||
value: [], | ||
deps: ['bar'], | ||
get: function (cerebral, deps, array) { | ||
return deps.bar; | ||
initialState: [], | ||
lookupState: ['bar'], | ||
get: function (cerebral, lookupState, array) { | ||
return lookupState.bar; | ||
} | ||
@@ -105,2 +106,2 @@ }; | ||
test.done(); | ||
}; | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2442663
65
4028
54
7