Setup react, photon-kit and flux architecture #12
Conversation
Huh, the build did not break - this is odd 😮 |
Not sure why travis wasn't getting the error I was getting locally, but running |
I think Redux is the recommended way to do things, but I don't have a ton of experience. I say go for it if you think it makes sense. |
@johndbritton I think I will - I read some blog posts comparing the different frameworks around, and everybody is praising Redux, even the engineers at Facebook. I'll start with it for now, but I'll take care to make as much of the logic Redux-agnostic as I can so we can change later if needed :) |
Hey @johndbritton @tarebyte - I think this is ready for review 🎉 |
@btmills @atareshawty would either of you mind giving this a 👀 over? |
@tarebyte I've only had a chance to skim a little. Here's one thing that I noticed though: When creating a component, the two ways I've experienced/read about are either Referencing (https://github.com/education/classroom-desktop/pull/12/files#diff-e406471fd3bfe87c9712e64367df9253) Using the later option would look something like this import React, { Component, PropTypes } from 'react';
import { Pane, ListGroup } from 'react-photonkit';
import Item from './Item';
export default class ItemList extends Component {
static propTypes = {
items: PropTypes.array.isRequired,
handleItemClick: PropTypes.func.isRequired
}
render() {
return (
<Pane>
<ListGroup>
{items.map(({ active, id, text }) =>
(
<Item
active={active}
handleClick={() => { this.props.handleItemClick(id) }}
key={id}
text={text}
/>
)
</ListGroup>
</Pane>
);
}
} Some benefits of this:
I have a small amount of experience, so @btmills would be the person to ask there. Hope I helped a little. I'm excited to see how this turns out 🚀 |
key={todo.id} | ||
text={todo.text} | ||
active={todo.active} | ||
handleClick={() => { handleItemClick(todo.id) }} |
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.
Creating functions inside the render method is usually an anti-pattern, as they'll be re-created on every render, putting unnecessary pressure on the garbage collector. (The react/jsx-no-bind lint rule talks a bit about it.)
From an idiomatic React perspective, this is really good code 👍 (@atareshawty - we use ES6 @NickTikhonov nice separation of container and presentational components. For functional components, I recommend checking out recompose. Specifically, the |
Thanks for having a glance over and for your suggestions @atareshawty @btmills :) I'll definitely take a look at recompose today and fix the issue before merging this in. |
@btmills - I enabled
I am guessing the above won't cause any problems on re-render since the VM can cache the unbound function - what do you think? I've taken a look at |
I'm going to merge this in and open up a new Issue/PR for a UI mock in React that implements the design proposed in #11 :) |
@NickTikhonov I can't say this for sure, but I'm guessing it wouldn't be able to cache because it needs to maintain a reference to the diff --git a/app/components/Item.jsx b/app/components/Item.jsx
index ea07c98..7d85863 100644
--- a/app/components/Item.jsx
+++ b/app/components/Item.jsx
@@ -1,20 +1,27 @@
import React, { PropTypes } from "react"
import {ListItem} from "react-photonkit"
+const handlers = window.handlers = new Set()
+
const Item = ({
text,
active,
handleClick
-}) => (
- <div
- onClick={handleClick}>
- <ListItem
- image="https://avatars3.githubusercontent.com/u/1744446?v=3&s=400"
- title={text}
- subtitle="subtitle"
- active={active} />
- </div>
-)
+}) => {
+ handlers.add(handleClick)
+ console.log(handlers.size)
+
+ return (
+ <div
+ onClick={handleClick}>
+ <ListItem
+ image="https://avatars3.githubusercontent.com/u/1744446?v=3&s=400"
+ title={text}
+ subtitle="subtitle"
+ active={active} />
+ </div>
+ )
+}
Item.propTypes = {
text: PropTypes.string.isRequired, The above diff adds code to keep track of the handlers that the diff --git a/app/components/Item.jsx b/app/components/Item.jsx
index ea07c98..ae58951 100644
--- a/app/components/Item.jsx
+++ b/app/components/Item.jsx
@@ -1,25 +1,40 @@
import React, { PropTypes } from "react"
import {ListItem} from "react-photonkit"
+import { withHandlers } from "recompose"
+
+const handlers = window.handlers = new Set()
+
+const enhance = withHandlers({
+ handleClick: (props) => () => {
+ props.handleClick(props.id)
+ }
+})
const Item = ({
text,
active,
handleClick
-}) => (
- <div
- onClick={handleClick}>
- <ListItem
- image="https://avatars3.githubusercontent.com/u/1744446?v=3&s=400"
- title={text}
- subtitle="subtitle"
- active={active} />
- </div>
-)
+}) => {
+ handlers.add(handleClick)
+ console.log(handlers.size)
+
+ return (
+ <div
+ onClick={handleClick}>
+ <ListItem
+ image="https://avatars3.githubusercontent.com/u/1744446?v=3&s=400"
+ title={text}
+ subtitle="subtitle"
+ active={active} />
+ </div>
+ )
+}
Item.propTypes = {
+ id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
active: PropTypes.bool.isRequired,
handleClick: PropTypes.func.isRequired
}
-export default Item
+export default enhance(Item)
diff --git a/app/components/ItemList.jsx b/app/components/ItemList.jsx
index d50880c..66d246a 100644
--- a/app/components/ItemList.jsx
+++ b/app/components/ItemList.jsx
@@ -12,9 +12,10 @@ const ItemList = ({
return (
<Item
key={todo.id}
+ id={todo.id}
text={todo.text}
active={todo.active}
- handleClick={function () { handleItemClick(todo.id) }}
+ handleClick={handleItemClick}
/>
)
})} If, instead, we use Removing the logging code, we end up with this: diff --git a/app/components/Item.jsx b/app/components/Item.jsx
index ea07c98..248f3a4 100644
--- a/app/components/Item.jsx
+++ b/app/components/Item.jsx
@@ -1,5 +1,14 @@
import React, { PropTypes } from "react"
import {ListItem} from "react-photonkit"
+import { withHandlers } from "recompose"
+
+const handlers = window.handlers = new Set()
+
+const enhance = withHandlers({
+ handleClick: (props) => () => {
+ props.handleClick(props.id)
+ }
+})
const Item = ({
text,
@@ -17,9 +26,10 @@ const Item = ({
)
Item.propTypes = {
+ id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
active: PropTypes.bool.isRequired,
handleClick: PropTypes.func.isRequired
}
-export default Item
+export default enhance(Item)
diff --git a/app/components/ItemList.jsx b/app/components/ItemList.jsx
index d50880c..66d246a 100644
--- a/app/components/ItemList.jsx
+++ b/app/components/ItemList.jsx
@@ -12,9 +12,10 @@ const ItemList = ({
return (
<Item
key={todo.id}
+ id={todo.id}
text={todo.text}
active={todo.active}
- handleClick={function () { handleItemClick(todo.id) }}
+ handleClick={handleItemClick}
/>
)
})} Hope this helps! 🍻 |
Awesome, thanks for the example! I think I now understand what is happening 🙌 . I'll be sure to use this pattern in the future. |
This PR deals with #8. Planned work:
Perhaps we can use something like Redux instead of implementing the architecture from scratch. What do you think @johndbritton @tarebyte ?
Side note: the build is going to break because of a bug in ESLint. A fix has been made but not published.