Skip to content

Commit ccae1b8

Browse files
committed
universal example
flavored some of the code with es6 to showcase how to use the HoC as a decorator package styles in uses https://github.com/jordangarcia/nuclear-js-react-addons
1 parent a920d01 commit ccae1b8

23 files changed

+950
-0
lines changed
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"stage": 0,
3+
"loose": "all"
4+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "./../../.eslintrc",
3+
"ecmaFeatures": {
4+
"jsx": true
5+
}
6+
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*bundle.js
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Isomorphic Flux Chat Example
2+
3+
This is the Facebook [flux-chat](https://github.com/facebook/flux/tree/master/examples/flux-chat)
4+
example re-written in NuclearJS to demonstate the differences in the libraries as well as to show how Getters are used.
5+
6+
## Running
7+
8+
You must have [npm](https://www.npmjs.org/) installed on your computer.
9+
From the root project directory run these commands from the command line:
10+
11+
`npm install`
12+
13+
This will install all dependencies.
14+
15+
To build the project, first run this command:
16+
17+
`npm run build` it will build the project.
18+
19+
Then run `npm start`. It will run the server on the port 1337, if you want another port use `npm start -- --port=9000` for example.
20+
21+
That's it. The client and server share everything except the entry file which is different for the client (`./js/main.js`) and server (`./server.js`).
22+
23+
Then open a browser and go to `http://localhost:1337` and you can expect the page while disabling javascript =).
24+
25+
Don't hesitate to run `npm run watch` and `npm start` in parallel to hack around.
26+
27+
## Considerations
28+
29+
Do not forget that React.render* functions are synchronous, it's up to the developers to actually make sure that the reactor is already filled before they call the render function.
30+
31+
One could for example make all actions return a promise or be callback based.
32+
33+
Then in the request server side, just wait for the data to be fetched somehow, and then render.
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
var mockData = require('./mock/messages')
2+
var Nuclear = require('nuclear-js')
3+
var NuclearAddons = require('nuclear-js-react-addons')
4+
var reactor = new Nuclear.Reactor({
5+
debug: process.env.NODE_ENV,
6+
})
7+
window.reactor = reactor
8+
var Chat = require('./modules/chat')
9+
10+
var ChatApp = require('./components/ChatApp.jsx')
11+
ChatApp = NuclearAddons.provideReactor(ChatApp)
12+
13+
Chat.register(reactor)
14+
15+
// @todo: refactor to use new nuclear methods when 1.1 lands ?
16+
if (window.window.reactor_state !== null) {
17+
reactor.__state = Nuclear.Immutable.fromJS(window.reactor_state)
18+
} else {
19+
Chat.actions.receiveAll(reactor, mockData)
20+
}
21+
22+
var React = require('react')
23+
window.React = React // export for http://fb.me/react-devtools
24+
25+
React.render(<ChatApp reactor={reactor}/>,
26+
document.getElementById('react')
27+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* This file is provided by Facebook for testing and evaluation purposes
3+
* only. Facebook reserves all rights not expressly granted.
4+
*
5+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
9+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
10+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11+
*/
12+
13+
var React = require('react')
14+
var MessageSection = require('./MessageSection.jsx')
15+
var ThreadSection = require('./ThreadSection.jsx')
16+
17+
/**
18+
* Styles
19+
*/
20+
require('../css/chatapp.css')
21+
22+
var ChatApp = React.createClass({
23+
render: function() {
24+
return (
25+
<div className="chatapp">
26+
<ThreadSection />
27+
<MessageSection />
28+
</div>
29+
)
30+
},
31+
})
32+
33+
module.exports = ChatApp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* This file is provided by Facebook for testing and evaluation purposes
3+
* only. Facebook reserves all rights not expressly granted.
4+
*
5+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
9+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
10+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11+
*/
12+
13+
var Chat = require('../modules/chat')
14+
var React = require('react')
15+
var NuclearMixin = require('nuclear-js-react-addons/nuclearMixin')
16+
17+
var ENTER_KEY_CODE = 13
18+
19+
// Difference with classic suggested architecture:
20+
// use the nuclear react mixin to have access to the
21+
// reactor in the context, that you can then use to
22+
// pass as first arguments of your actions
23+
24+
var MessageComposer = React.createClass({
25+
mixins: [NuclearMixin],
26+
27+
propTypes: {
28+
threadID: React.PropTypes.string.isRequired,
29+
},
30+
31+
getInitialState: function() {
32+
return {text: ''}
33+
},
34+
35+
render: function() {
36+
return (
37+
<textarea
38+
className="message-composer"
39+
name="message"
40+
value={this.state.text}
41+
onChange={this._onChange}
42+
onKeyDown={this._onKeyDown}
43+
/>
44+
)
45+
},
46+
47+
_onChange: function(event, value) {
48+
this.setState({text: event.target.value})
49+
},
50+
51+
_onKeyDown: function(event) {
52+
if (event.keyCode === ENTER_KEY_CODE) {
53+
event.preventDefault()
54+
var text = this.state.text.trim()
55+
if (text) {
56+
Chat.actions.createMessage(this.context.reactor, text, this.props.threadID)
57+
}
58+
this.setState({text: ''})
59+
}
60+
},
61+
})
62+
63+
module.exports = MessageComposer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* This file is provided by Facebook for testing and evaluation purposes
3+
* only. Facebook reserves all rights not expressly granted.
4+
*
5+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
9+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
10+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11+
*/
12+
13+
var React = require('react')
14+
15+
var ReactPropTypes = React.PropTypes
16+
17+
var MessageListItem = React.createClass({
18+
propTypes: {
19+
message: ReactPropTypes.object,
20+
},
21+
22+
render: function() {
23+
var message = this.props.message
24+
/**
25+
* This next line may cause a problem of reconciliation between server markup
26+
* and client markup because of i18n settings, causing this warning:
27+
*
28+
* (client) kjb6wao.0.1.$t_1.1">6:29:19 PM</div><div
29+
* (server) kjb6wao.0.1.$t_1.1">18:29:19</div><div c
30+
*
31+
* Figure it out =) (hint: consistency is key >.>)
32+
*/
33+
var dateString = (new Date(message.get('timestamp'))).toLocaleTimeString()
34+
35+
return (
36+
<li className="message-list-item">
37+
<h5 className="message-author-name">{message.get('authorName')}</h5>
38+
<div className="message-time">
39+
{dateString}
40+
</div>
41+
<div className="message-text">{message.get('text')}</div>
42+
</li>
43+
)
44+
},
45+
})
46+
47+
module.exports = MessageListItem
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* This file is provided by Facebook for testing and evaluation purposes
3+
* only. Facebook reserves all rights not expressly granted.
4+
*
5+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
9+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
10+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11+
*/
12+
13+
var MessageComposer = require('./MessageComposer.jsx')
14+
var MessageListItem = require('./MessageListItem.jsx')
15+
var Chat = require('../modules/chat')
16+
var React = require('react')
17+
var NuclearMixin = require('nuclear-js-react-addons/nuclearMixin')
18+
19+
var MessageSection = React.createClass({
20+
mixins: [NuclearMixin],
21+
22+
getDataBindings() {
23+
return {
24+
thread: Chat.getters.currentThread,
25+
}
26+
},
27+
28+
componentDidMount: function() {
29+
this._scrollToBottom()
30+
},
31+
32+
render: function() {
33+
var thread = this.state.thread
34+
var messageListItems = thread.get('messages').map(message => {
35+
return (
36+
<MessageListItem
37+
key={message.get('id')}
38+
message={message}
39+
/>
40+
)
41+
})
42+
43+
return (
44+
<div className="message-section">
45+
<h3 className="message-thread-heading">{thread.get('threadName')}</h3>
46+
<ul className="message-list" ref="messageList">
47+
{messageListItems}
48+
</ul>
49+
<MessageComposer threadID={thread.get('threadID')}/>
50+
</div>
51+
)
52+
},
53+
54+
componentDidUpdate: function() {
55+
this._scrollToBottom()
56+
},
57+
58+
_scrollToBottom: function() {
59+
var ul = this.refs.messageList.getDOMNode()
60+
ul.scrollTop = ul.scrollHeight
61+
},
62+
})
63+
64+
module.exports = MessageSection
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* This file is provided by Facebook for testing and evaluation purposes
3+
* only. Facebook reserves all rights not expressly granted.
4+
*
5+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
9+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
10+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11+
*/
12+
13+
var React = require('react')
14+
var Chat = require('../modules/chat')
15+
var cx = require('react/lib/cx')
16+
// var NuclearMixin = require('nuclear-js-react-addons/nuclearMixin')
17+
var nuclearComponent = require('nuclear-js-react-addons/nuclearComponent')
18+
19+
var ReactPropTypes = React.PropTypes
20+
21+
var ThreadListItem = React.createClass({
22+
// mixins: [NuclearMixin], // mixin use
23+
24+
propTypes: {
25+
thread: ReactPropTypes.object,
26+
currentThreadID: ReactPropTypes.string,
27+
},
28+
29+
render: function() {
30+
var thread = this.props.thread
31+
var lastMessage = thread.get('messages').last()
32+
var dateString = (new Date(lastMessage.get('timestamp'))).toLocaleTimeString()
33+
return (
34+
<li
35+
className={cx({
36+
'thread-list-item': true,
37+
'active': thread.get('threadID') === this.props.currentThreadID,
38+
})}
39+
onClick={this._onClick}>
40+
<h5 className="thread-name">{thread.get('threadName')}</h5>
41+
<div className="thread-time">
42+
{dateString}
43+
</div>
44+
<div className="thread-last-message">
45+
{lastMessage.get('text')}
46+
</div>
47+
</li>
48+
)
49+
},
50+
51+
_onClick: function() {
52+
var threadID = this.props.thread.get('threadID')
53+
if (this.props.currentThreadID !== threadID) {
54+
/**
55+
* If you use the mixin, dataBindings is state and reactor is in context,
56+
* if you use the HoC nuclearComponent, both are props
57+
*/
58+
// Chat.actions.clickThread(this.context.reactor, threadID) // with mixin
59+
Chat.actions.clickThread(this.props.reactor, threadID) // with HoC
60+
}
61+
},
62+
})
63+
64+
// HoC use with
65+
module.exports = nuclearComponent(ThreadListItem/*, optionalDataBindingsObject */)

0 commit comments

Comments
 (0)