-
Notifications
You must be signed in to change notification settings - Fork 320
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
07745f5
commit efbd652
Showing
8 changed files
with
664 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/** | ||
* AltContainer. | ||
* | ||
* There are many ways to use AltContainer. | ||
* | ||
* Using the `stores` prop. | ||
* | ||
* <AltContainer stores={{ FooStore: FooStore }}> | ||
* children get this.props.FootStore.storeData | ||
* </AltContainer> | ||
* | ||
* You can also pass in functions. | ||
* | ||
* <AltContainer stores={{ FooStore: function () { return { storeData: true } } }}> | ||
* children get this.props.FootStore.storeData | ||
* </AltContainer> | ||
* | ||
* Using the `store` prop. | ||
* | ||
* <AltContainer store={FooStore}> | ||
* children get this.props.storeData | ||
* </AltContainer> | ||
* | ||
* Passing in `flux` because you're using alt instances | ||
* | ||
* <AltContainer flux={flux}> | ||
* children get this.props.flux | ||
* </AltContainer> | ||
* | ||
* Using a custom render function. | ||
* | ||
* <AltContainer | ||
* render={function (props) { | ||
* return <div />; | ||
* }} | ||
* /> | ||
* | ||
* Full docs available at http://goatslacker.github.io/alt/ | ||
*/ | ||
var React = require('react/addons') | ||
var Subscribe = require('../mixins/Subscribe') | ||
var assign = require('object-assign') | ||
|
||
var cloneWithProps = React.addons.cloneWithProps | ||
|
||
function getState(store, props) { | ||
return typeof store === 'function' ? store(props) : store.getState() | ||
} | ||
|
||
var AltContainer = React.createClass({ | ||
getInitialState: function () { | ||
if (this.props.stores && this.props.store) { | ||
throw new ReferenceError('Cannot define both store and stores') | ||
} | ||
|
||
return this.getStateFromStores() || {} | ||
}, | ||
|
||
componentDidMount: function () { | ||
Subscribe.create(this) | ||
|
||
if (this.props.store) { | ||
this.addSubscription(this.props.store) | ||
} else if (this.props.stores) { | ||
var stores = this.props.stores | ||
|
||
if (Array.isArray(stores)) { | ||
stores.forEach(function (store) { | ||
this.addSubscription(store) | ||
}, this) | ||
} else { | ||
Object.keys(stores).forEach(function (formatter) { | ||
this.addSubscription(stores[formatter]) | ||
}, this) | ||
} | ||
} | ||
}, | ||
|
||
componentWillUnmount: function () { | ||
Subscribe.destroy(this) | ||
}, | ||
|
||
getStateFromStores: function () { | ||
if (this.props.store) { | ||
return getState(this.props.store, this.props) | ||
} else if (this.props.stores) { | ||
var stores = this.props.stores | ||
|
||
// If you pass in an array of stores the state is merged together. | ||
if (Array.isArray(stores)) { | ||
return stores.reduce(function (obj, store) { | ||
return assign(obj, getState(store, this.props)) | ||
}.bind(this), {}) | ||
|
||
// if it is an object then the state is added to the key specified | ||
} else { | ||
return Object.keys(stores).reduce(function (obj, key) { | ||
obj[key] = getState(stores[key], this.props) | ||
return obj | ||
}.bind(this), {}) | ||
} | ||
} else { | ||
return {} | ||
} | ||
}, | ||
|
||
addSubscription(store) { | ||
if (typeof store === 'object') { | ||
Subscribe.add(this, store, this.altSetState) | ||
} | ||
}, | ||
|
||
altSetState: function () { | ||
this.setState(this.getStateFromStores()) | ||
}, | ||
|
||
getProps: function () { | ||
return assign( | ||
this.context.flux || this.props.flux | ||
? { flux: this.context.flux || this.props.flux } | ||
: {}, | ||
this.state | ||
) | ||
}, | ||
|
||
render: function () { | ||
var children = this.props.children | ||
|
||
// Custom rendering function | ||
if (typeof this.props.render === 'function') { | ||
return this.props.render(this.getProps()) | ||
} | ||
|
||
// Does not wrap child in a div if we don't have to. | ||
if (Array.isArray(children)) { | ||
return React.createElement('div', null, children.map(function (child, i) { | ||
return cloneWithProps(child, assign({ key: i }, this.getProps())) | ||
}, this)) | ||
} else if (children) { | ||
return cloneWithProps(children, this.getProps()) | ||
} else { | ||
return React.createElement('div', this.getProps()) | ||
} | ||
} | ||
}) | ||
|
||
module.exports = AltContainer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
--- | ||
layout: docs | ||
title: AltContainer | ||
description: A Store Controller component for better composition | ||
permalink: /docs/components/altContainer | ||
--- | ||
|
||
# AltContainer | ||
|
||
AltContainer is not an idea exclusive to alt. For more information on why you should be using container components check out React.js Conf 2015 talk [Making your app fast with high-performance components](https://youtu.be/KYzlpRvWZ6c?t=22m48s). | ||
|
||
The basic idea is that you have a container that wraps your component, the duty of this container component is to handle all the data fetching and communication with the stores, it then renders the corresponding children. The sub-components just render markup and are data agnostic thus making them highly reusable. | ||
|
||
## `stores` | ||
|
||
You can pass in an object to `stores` where the keys correspond to the prop that the children will receive and their value the store we should retrieve the state from. | ||
|
||
For example: | ||
|
||
```js | ||
<AltContainer | ||
stores={{ | ||
BlogPosts: BlogStore, | ||
Comments: CommentsStore, | ||
Shares: ShareStore | ||
}} | ||
> | ||
<div /> | ||
</AltContainer> | ||
``` | ||
|
||
Will pass the state from `FooStore` (`FooStore.getState()`) into the `<div />` as `this.props.FooStore`. `BarStore` will be available in `this.props.BarStore`, etc. | ||
|
||
You can pass in a custom function as the value in order to control what each prop will represent. Say you have multiple getters on a store and only want to pass a subset of the state rather than the whole state. | ||
|
||
```js | ||
<AltContainer | ||
stores={{ | ||
post: function (props) { | ||
return BlogStore.getPostFor(props.blogId); | ||
}, | ||
comments: function (props) { | ||
return CommentsStore.getCommentsFor(props.blogId) | ||
}, | ||
shares: function (props) { | ||
return ShareStore.getSharesFor(props.blogId) | ||
} | ||
}} | ||
> | ||
<BlogPost /> | ||
</AltContainer> | ||
``` | ||
|
||
## `store` | ||
|
||
If you only have a single store you can use `store` to bind it. The state will then be passed as props straight through. | ||
|
||
```js | ||
var PostStore = alt.createStore({ | ||
displayName: 'PostStore', | ||
state: { | ||
id: 1, | ||
title: 'Hello, World!' | ||
} | ||
}); | ||
|
||
<AltContainer store={PostStore}> | ||
<BlogPost /> | ||
</AltContainer> | ||
``` | ||
|
||
`BlogPost` in this case will receive all the state of `BlogStore` as props. `this.props.id` and `this.props.title` will be passed through to `BlogPost`. | ||
|
||
Just like `stores`, you can define your own custom function to use with `store`. | ||
|
||
```js | ||
|
||
function blogStoreFetcher(props) { | ||
return BlogStore.getPostFor(props.blogId); | ||
} | ||
|
||
<AltContainer store={blogStoreFetcher}> | ||
<BlogPost /> | ||
</AltContainer> | ||
``` | ||
|
||
Children will get the properties you define. | ||
|
||
```js | ||
<AltContainer store={BlogStore}> | ||
<BlogPost className="my-awesome-post" /> | ||
</AltContainer> | ||
``` | ||
|
||
## `render` | ||
|
||
But you can also specify a special `render` function which will render whatever your heart desires. | ||
|
||
```js | ||
<AltContainer | ||
store={BlogStore} | ||
render={function (props) { | ||
return <BlogPost className="my-awesome-post" id={props.id} title="Overriding the title" /> | ||
}} | ||
/> | ||
``` | ||
|
||
## `flux` | ||
|
||
`AltContainer` also works with Alt instances. If you're passing the alt instance around as a `flux` prop, it'll bind it to all its children. | ||
|
||
```js | ||
const flux = new Flux(); | ||
|
||
<AltContainer flux={flux}> | ||
<Header /> | ||
<Body /> | ||
<Footer /> | ||
</AltContainer> | ||
``` | ||
|
||
Header, Body, and Footer will have the `flux` context passed down. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.