data flow in react application - javascript

I am currently learning react and I have run into the problem of elegently extracting states from my components.
basically I have a few components which contains forms and other inputs from which I need the data in my business logic, the data I need is coupled with the state of the component. From what I understand the data should have unidirectional flow but I can't think of how I can make my data flow back towards my business logic. I could just make some interface functions which call the respective, but I feel this would violate the unidirectional flow.
anyhelp with some examples would be greatly appreciated!

You typically pass down callbacks from parent components to child components as props. When the state changes in any of the child components, it invokes that callback and passes whatever data is appropriate in each use case. Your "controller-view" (the root component that implements the actual callback) then does whatever business logic you need based on the current state and then updates its state accordingly (causing a re-render of the component tree from that component down). Read the docs about component communication.
Something like this:
var Child = React.createClass({
onTextChange: function() {
var val = 13; // somehow calculate new value
this.props.onTextChange(val);
},
render: function() {
return <input type="text" value={this.props.val} onChange={this.onTextChange} />
}
});
var Parent = React.createClass({
onTextChange: function(val) {
var newVal = someBusinessLogic(val);
this.setState({val: newVal});
},
render: function() {
return <Child onTextChange={this.onTextChange} val={this.state.val} />
}
});

The best way to work with data flow in React is to use the Flux pattern. You need some time to understand how it works, but it will make your life much easier as your project grows.
Look at some Flux tutorial, for example this one (using the Alt flux implementation): https://reactjsnews.com/getting-started-with-flux/

Related

_React Js one page application

I am looking to create a one page application with ReactJS.
Is it advisable to combine it with angular or it is suitable just on its own? I would like to populate the one page site with sections - adding various features like carousels, sliders, isotope filters ...
<!DOCTYPE html>
<html>
<head>
<title>React Js one page</title>
<script src="https://fb.me/react-with-addons-0.14.7.min.js"></script>
<script src="https://fb.me/react-dom-0.14.7.min.js"></script>
</head>
<body>
<section>
One
<script>
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="Colonel Mustard" />, mountNode);
</script>
</section>
<section>
Two
<script>
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
ReactDOM.render(<CommentBox />, mountNode);
</script>
</section>
<section>
Three
<script>
"use strict";
var MarkdownEditor = React.createClass({
displayName: "MarkdownEditor",
getInitialState: function getInitialState() {
return { value: 'Type some *markdown* here!' };
},
handleChange: function handleChange() {
this.setState({ value: this.refs.textarea.value });
},
rawMarkup: function rawMarkup() {
return { __html: marked(this.state.value, { sanitize: true }) };
},
render: function render() {
return React.createElement(
"div",
{ className: "MarkdownEditor" },
React.createElement(
"h3",
null,
"Input"
),
React.createElement("textarea", {
onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value }),
React.createElement(
"h3",
null,
"Output"
),
React.createElement("div", {
className: "content",
dangerouslySetInnerHTML: this.rawMarkup()
})
);
}
});
ReactDOM.render(React.createElement(MarkdownEditor, null), mountNode);
</script>
</section>
</body>
</html>
If you're just starting out with React, I'd highly recommend following Pete Hunt's advice:
You’ll know when you need Flux. If you aren’t sure if you need it, you don’t need it.
The best thing to do is get started with React by itself and manage application state using the local state that comes with each of your components.
When you find that you start having to pass data back up to parent components, then add Flux into the mix and rewrite your stateful components to instead use Flux stores.
We'll look at how to tackle writing a simplified version of the StackOverflow answer component as a React application from the bottom up.
Notice, I said React application, not React component. This is because there's no technical difference. A React application is a big React component made up of lots of smaller ones.
Identify Components
Once you have an interface for your application (anything from wireframes to html/css) you can visually subdivide them to work out how they'll fit together as React components.
There are no hard and fast rules about how exactly you decide what should or should not be it's own component, but you'll get a feeling for it the more times you do it.
is <Answer />
is <Votes />
is <AnswerText />
is <AnswerActions />
Because we're building from the bottom up, we'd start by implementing each of the child components and testing that they work alone.
At this point in the development lifecycle we'd just write static markup for each component. There's no need to think about props or state yet.
We can use the stateless component syntax to get started on the components we've identified. Here's an example of how we might write the <Votes /> component.
function Votes() {
return (
<div>
<a>▲</a>
<strong>0</strong>
<a>▼</a>
</div>
);
}
Of course this doesn't do anything, but it allows us to start composing our components to get a feel for the structure of the application.
We can render this into the DOM to check that it works at any time.
ReactDOM.render(<Votes />, document.getElementById('app'));
Once you'd finished implementing static versions of the other components, you could put them together to create the parent <Answer /> component.
function Answer() {
return (
<div>
<Votes />
<AnswerText />
<AnswerActions />
</div>
);
}
Design Data Flow
The next thing to do is to figure out how data flows through your application.
At this point we can create some dummy data in the form of an answer object that looks something like this:
{
"id": 0,
"votes": 0,
"text": "This is an answer"
}
Initially we can render the <Answer /> component by passing this answer object to it as a prop.
<Answer answer={answer} />
Now it's that components job to pass down the appropriate data to its children.
Obviously not each child needs all of the data though, so we'll have to decide what data goes where. Let's update our <Answer /> component.
function Answer(props) {
var answer = props.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
The <Votes /> component needs know the current number of votes and it also needs to know the id of the answer so that it can communicate change to the server.
The <AnswerText /> component just renders a block of text, so that's all we need to pass it.
Finally, the <AnswerActions /> component renders a list of links that allow the user to perform some action (share, edit, flag) on the answer. This component also needs the answer's id so that it can communicate with the server.
Now we have to update these child components in turn to use these new dynamic values, instead of the static values we used at first. We'll revisit the <Votes /> component to see this happen.
function Votes(props) {
var urls = {
upvote: '/api/answers/' + props.id + '/upvote',
downvote: '/api/answers/' + props.id + '/downvote'
};
return (
<div>
<a href={urls.upvote}>▲</a>
<strong>{props.votes}</strong>
<a href={urls.downvote}>▼</a>
</div>
);
}
Now our vote component will make a HTTP request to the appropriate endpoint when we click on the vote buttons, however, we'd rather make this update without reloading and re-rendering the entire application.
Identify Stateful Components
The final piece of the component development process is to identify stateful components. These components have moving parts and data that will change during the lifetime of the application.
Each time the state inside a component changes, the entire component re-renders. We can revisit the wireframes to see which of our components needs to manage changing data.
This application only has one stateful component () and that's `. When we click on one of the arrows, we need to update the number to reflect the new count.
It's the only one of our components that ever needs to re-render.
This means we'll need to upgrade the component to use React's createClass syntax. This allows it to start managing it's own state.
var Votes = React.createClass({
getInitialState: function() {
return { votes: this.props.votes };
},
upvote: function() {
var newVotes = this.state.votes + 1;
this.setState({
votes: newVotes
});
},
downvote: function() {
var newVotes = this.state.votes - 1;
this.setState({
votes: newVotes
});
},
render: function() {
return (
<div>
<a onClick={this.upvote}>▲</a>
<strong>{this.state.votes}</strong>
<a onClick={this.downvote}>▼</a>
</div>
);
}
});
I've jumped the gun a bit and implemented the full component, but hopefully you'll get the idea.
First we use getInitialState to set up some state to represent the initial number of votes in the component.
Next we implement upvote and downvote component methods that update the component state.
Finally we re-implement the render method from before, but have the arrows trigger the new component methods, not page requests.
Each time we make a call to setState, React will re-render the component. Hopefully you can see why we put the state in the <Votes /> component and not the <Answer /> component. It would be crazy to re-render the answer text and actions, just because the votes had changed.
Flux It Up
Once we've identified and implemented all of our stateful components, we can start to move their state out into Flux stores.
It's much more likely that a real application would have an <AnswerStore /> than a <VoteStore />, so that's what we'll implement. For now we'll just keep mocking our data.
var AnswerStore = {
_listeners: [],
_answers: {
"0": {
"id": 0,
"votes": 0,
"text": "This is an answer"
}
},
get: function(id) {
return this._answers[id];
},
update: function(id, update) {
var answer = this.get(id);
var updatedAnswer = Object.assign({}, answer, update);
this._answers[id] = updatedAnswer;
this.emit();
},
listen: function(f) {
this._listeners.push(f);
},
emit: function() {
this._listeners.forEach(function(f) {
f();
});
}
};
In this example, I've written a fairly generic store that contains data, provides simple handlers for listening to model changes and finally exposes methods for mutating the data in the store.
It's important that our update method treats the individual answers as immutable in this application, otherwise we risk mutating an object that other parts of the application have a reference to, causing the object to change underneath them. We use Object.assign to create a new object each time, based on the old one.
The next thing we need to do is set up some action handlers for this store.
dispatcher.register(function(action) {
switch(action.type) {
case 'UPVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes + 1 });
break;
case 'DOWNVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes - 1 });
break;
}
});
This simply wires the update method to two actions called 'UPVOTE' and 'DOWNVOTE'
Now we connect Flux to our <AnswerComponent /> which needs to be re-written in the long form.
var Answer = React.createClass({
getInitialState: function() {
return { answer: {} };
},
componentWillMount: function() {
this.update();
AnswerStore.listen(this.update);
},
update: function() {
var id = this.props.id;
this.setState({ answer: AnswerStore.get(id) });
},
render: function() {
var answer = this.state.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
});
In our componentWillMount method we fetch our initial data for the store, then set up a listener on the store that fetches and updates the component state, whenever the store changes.
Finally, we need a way to dispatch the appropriate actions from our <Votes /> component.
The most popular way to do this is with action creators. An action creator is a function which takes some data as parameters, then packages it up and dispatches it as an action.
var Actions = {
upvote: function(id) {
dispatcher.dispatch({
type: 'UPVOTE',
id: id
});
},
downvote: function(id) {
dispatcher.dispatch({
type: 'DOWNVOTE',
id: id
});
}
};
Then we call these actions from inside our <Votes /> component (which can become stateless again).
function Votes(props) {
var id = props.id;
return (
<div>
<a onClick={Actions.upvote.bind(null, id)}>▲</a>
<strong>{props.votes}</strong>
<a onClick={Actions.downvote.bind(null, id)}>▼</a>
</div>
);
}
This component now uses the action creators to dispatch actions for our Flux store(s) to handle.
If we look at the flow of data through our application, we can see that we now have a unidirectional cycle, rather than a tree.
The <Answer /> component passes the id down to the <Votes /> component.
The <Votes /> component dispatches an action using that id.
The AnswerStore processes the action and emits a change.
The <Answer /> component hears the update and updates its state, re-rendering its children.
Here's a jsfiddle of this demo application.
Scale Up
This is a very simple component that only handles a tiny amount of data flow and even less application state, however, it's enough to show you how to compose a React component and that's all you need to build a React app.
Let's imagine we were implementing a StackOverflow question as a React application.
function App() {
return (
<Page>
<Navigation />
<SideBar>
<MetaDetails />
<Ads />
<RelatedQuestions />
</SideBar>
<Question />
<AnswerList />
<AnswerEditor />
<Footer />
</Page>
);
}
It might seem like a complex application, but you can break it down and express it as distinct components, then you can implement and test the components individually, just like we did here and bring them altogether to form a complex application.
Don't Over Complicate
For most simple React applications like this one, Flux is not actually necessary. It's worth remembering that React was released over a year before Flux and yet it was adopted by a lot of developers regardless.
Of course, I've only really covered structuring and implementing components here. Taking an application from wireframes to deployment is a much more complicated process and there's no way it could be covered in detail in one answer. In time, you'll probably also want to learn about:
Package management and dependencies
Module bundlers
Routing
Hot Reloading
ES6/Babel
Redux
Server Side Rendering
Immutable Data
Inline Styles
Relay/Falcor/GraphQL
It takes some time to get through this list of things, the trick is not to rush. Don't overcomplicate your existing project until you find the reasons that these solutions exist, naturally.
I think react-howto is the best guide out there. Although it's not heavy on detail, it links to a lot of good resources and most importantly it provides an opinionated guide to the order in which you should learn these technologies on your way to becoming a competent ReactJS developer.
The choice of framework (Angular/React) does not prevent building any of the functionality you described, and your site could be built with neither, either, or both of those frameworks.
While you certainly could combine Angular and React, I'm not sure why you would want to, and it's not going to be the easiest of tasks. It's doable, of course, but it's going to provide a lot of difficulty for very little ultimate gain.
If you want to build a SPA with React, I would focus more on finding a Flux implementation that you like, and learning how to integrate that. Flux is designed specifically with React in mind to handle large SPAs which might have complicated data flow, and it's certainly a tool that is easier to incorporate early on.
The only other library I would consider out of the gate would be Immutable.js, which pairs very well with both React and Flux.
But otherwise, until you find a need to bring in additional frameworks/libraries, attempt to hold off. With all of the exciting JS frameworks out there, it's tempting to want to use them all, but in reality you're better off picking one to focus on, and then maybe bringing in some tools from another later on when they are necessitated.

When to use state and when props?

I am studying the principles of react.
According to some reviews, some people says is better to keep your component stateless, what does it mean?
But other people says, that if you need to update your component, then you should learn how to set your state to the proper state.
I saw this.props / this.setProps and this.state / this.setState and I am confuse with that.
Something I am trying to figure is, how can I update a component by itself and not from a parent component? should I use props or state in this case?
I already read some docs about props and state, what I don't have clear, is: when to use one or another ?
Props vs. state comes down to "who owns this data?"
If data is managed by one component, but another component needs access to that data, you'd pass the data from the one component to the other component via props.
If a component manages the data itself, it should use state and setState to manage it.
So the answer to
how can I update a component by itself and not from a parent component? should I use props or state in this case?
is to use state.
Props should be considered immutable and should never be changed via mutation. setProps is only useful on a top-level component and generally should not be used at all. If a component passes another component a property, and the first component wants the second to be able to change it, it should also pass it a function property that the second component can call to ask the first component to update its state. For example:
var ComponentA = React.createClass({
getInitialState: function() {
return { count: 0 };
},
render: function() {
return <Clicker count={this.state.count} incrementCount={this.increment} />;
},
increment: function() {
this.setState({count: this.state.count + 1});
}
});
// Notice that Clicker is stateless! It's only job is to
// (1) render its `count` prop, and (2) call its
// `incrementCount` prop when the button is clicked.
var Clicker = React.createClass({
render: function() {
// clicker knows nothing about *how* to update the count
// only that it got passed a function that will do it for it
return (
<div>
Count: {this.props.count}
<button onClick={this.props.incrementCount}>+1</button>
</div>
);
}
});
(Working example: https://jsbin.com/rakate/edit?html,js,output)
For and object-oriented programming analogy, think of a class/object: state would be the properties you put on the class; the class is free to update those as it sees fit. Props would be like arguments to methods; you should never mutate arguments passed to you.
Keeping a component "stateless" means that it doesn't have any state, and all its rendering is based on its props. Of course, there has to be state somewhere or else your app won't do anything! So this guideline is basically saying to keep as many components as possible stateless, and only manage the state in as few top-level components as possible.
Keeping components stateless makes them easier to understand, reuse, and test.
See A brief interlude: props vs state in the React docs for more information.
Use state when you know the variable value is going to affect the view. This is particularly critical in react, because whenever the state variable changes there is a rerender(though this is optimized with the virtual DOM, you should minimize it if you can), but not when a prop is changed (You can force this, but not really needed).
You can use props for holding all other variables, which you think can be passed into the component during the component creation.
If you have want to make a multi-select dropdown called MyDropdown for example
state = {
show: true,
selected:[],
suggestions:this.props.suggestionArr.filter((i)=>{
return this.state.suggestions.indexOf(i)<0;
})
}
props={
eventNamespace:'mydropdown',
prefix : 'm_',
suggestionArr:[],
onItemSelect:aCallbackFn
}
As you can see, the objects in the state variable are going to affect the view some way or the other.
The objects in the props are mostly objects which should remain the same throughout the component life cycle. So these objects can be callback functions, strings used to namespace events or other holders.
So if you do want to update the component by itself, you need to have to look into how componentWillRecieveProps ,componentWillUpdate, componentDidUpdate and componentShouldUpdate works. More or less, this depends on the requirement and you can use these lifecycle methods to ensure that the rendering is within the component and not in the parent.

React.js: Difference between passing in the subcomponents with React.render() and with React.createClass()'s render?

I'm working on a project using React.js, and very confused about the composition of React.
http://facebook.github.io/react/docs/multiple-components.html
the link above gives an example. It uses React.creatClass() create three components. A parent component and two child components. The parent component includes the others within it's JSX in the render method.
This example's very clear, but not very 'reusable'. What if I wanna pass in another child in another situation? React.js seems lacking the 'extend' method like Backbone's view.
later, I found that you can pass children components in the React.render(), and use this.props.children to composite.
var Tom = React.createClass({
render: function(){
return(
<a>This is Tom.</a>
)
}
});
var John = React.createClass({
render: function(){
return(
<a>This is John.</a>
)
}
});
var Outter = React.createClass({
componentDidMount:function(){
console.log(this.props.children);
},
render: function(){
return(
<div className="test">
{this.props.children}
</div>
)
}
});
React.render(<Outter><Tom /><John /></Outter>, document.getElementById('main'));
I think that's great but what's the really difference between this method and the example above? is this method the right way to composite components in React.js?
thanks
The difference is just like you said, that you can pass any components you'd like as children of that component. Components that use this.props.children are usually components that acts as wrappers for style and behaviour, but the contents of the component changes for different use cases. Like a popup, where you want the same look and behaviour (like a close button) for every popup, but the contents of the popup is different for every popup.
Components that don't use this.props.children are more like black boxes, they know everything about how they should be rendered and what child components they need. But you can still make them dynamic by passing other props to them.
Components can be also be passed as props. Components are just JS objects, and any JS object can be passed as a prop. But I don't think I've ever seen a use case for doing it. It might make some sense if you have a wrapper component with two or more specific "slots" that should be rendered to. Something like:
var Wrapper = require('./wrapper');
var Header = require('./title');
var Content = require('./content');
var Footer = require('./footer');
var MyComponent = React.createClass({
render() {
return <Wrapper footer={Footer} header={Header} content={Content} />;
}
});
It depends on your use-case, take this two examples of forms:
a login form - almost all login forms have the same structure, username/email, password, a keep me logged in checkbox, and a login button; a LoginForm component doesn't need to be further customised in regards to its structure so it's suitable to be used as is
a signup form - now depending on context you might give more or less fields to the user to complete when signing up, and in this case you use props.children

At what nesting level should components read entities from Stores in Flux?

I'm rewriting my app to use Flux and I have an issue with retrieving data from Stores. I have a lot of components, and they nest a lot. Some of them are large (Article), some are small and simple (UserAvatar, UserLink).
I've been struggling with where in component hierarchy I should read data from Stores.
I tried two extreme approaches, neither of which I quite liked:
All entity components read their own data
Each component that needs some data from Store receives just entity ID and retrieves entity on its own.
For example, Article is passed articleId, UserAvatar and UserLink are passed userId.
This approach has several significant downsides (discussed under code sample).
var Article = React.createClass({
mixins: [createStoreMixin(ArticleStore)],
propTypes: {
articleId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
article: ArticleStore.get(this.props.articleId);
}
},
render() {
var article = this.state.article,
userId = article.userId;
return (
<div>
<UserLink userId={userId}>
<UserAvatar userId={userId} />
</UserLink>
<h1>{article.title}</h1>
<p>{article.text}</p>
<p>Read more by <UserLink userId={userId} />.</p>
</div>
)
}
});
var UserAvatar = React.createClass({
mixins: [createStoreMixin(UserStore)],
propTypes: {
userId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
user: UserStore.get(this.props.userId);
}
},
render() {
var user = this.state.user;
return (
<img src={user.thumbnailUrl} />
)
}
});
var UserLink = React.createClass({
mixins: [createStoreMixin(UserStore)],
propTypes: {
userId: PropTypes.number.isRequired
},
getStateFromStores() {
return {
user: UserStore.get(this.props.userId);
}
},
render() {
var user = this.state.user;
return (
<Link to='user' params={{ userId: this.props.userId }}>
{this.props.children || user.name}
</Link>
)
}
});
Downsides of this approach:
It's frustrating to have 100s components potentially subscribing to Stores;
It's hard to keep track of how data is updated and in what order because each component retrieves its data independently;
Even though you might already have an entity in state, you are forced to pass its ID to children, who will retrieve it again (or else break the consistency).
All data is read once at the top level and passed down to components
When I was tired of tracking down bugs, I tried to put all data retrieving at the top level. This, however, proved impossible because for some entities I have several levels of nesting.
For example:
A Category contains UserAvatars of people who contribute to that category;
An Article may have several Categorys.
Therefore if I wanted to retrieve all data from Stores at the level of an Article, I would need to:
Retrieve article from ArticleStore;
Retrieve all article's categories from CategoryStore;
Separately retrieve each category's contributors from UserStore;
Somehow pass all that data down to components.
Even more frustratingly, whenever I need a deeply nested entity, I would need to add code to each level of nesting to additionally pass it down.
Summing Up
Both approaches seem flawed. How do I solve this problem most elegantly?
My objectives:
Stores shouldn't have an insane number of subscribers. It's stupid for each UserLink to listen to UserStore if parent components already do that.
If parent component has retrieved some object from store (e.g. user), I don't want any nested components to have to fetch it again. I should be able to pass it via props.
I shouldn't have to fetch all entities (including relationships) at the top level because it would complicate adding or removing relationships. I don't want to introduce new props at all nesting levels each time a nested entity gets a new relationship (e.g. category gets a curator).
Most people start out by listening to the relevant stores in a controller-view component near the top of the hierarchy.
Later, when it seems like a lot of irrelevant props are getting passed down through the hierarchy to some deeply nested component, some people will decided it's a good idea to let a deeper component listen for changes in the stores. This offers a better encapsulation of the problem domain that this deeper branch of the component tree is about. There are good arguments to be made for doing this judiciously.
However, I prefer to always listen at the top and simply pass down all the data. I will sometimes even take the entire state of the store and pass it down through the hierarchy as a single object, and I will do this for multiple stores. So I would have a prop for the ArticleStore's state, and another for the UserStore's state, etc. I find that avoiding deeply nested controller-views maintains a singular entry point for the data, and unifies the data flow. Otherwise, I have multiple sources of data, and this can become difficult to debug.
Type checking is more difficult with this strategy, but you can set up a "shape", or type template, for the large-object-as-prop with React's PropTypes. See:
https://github.com/facebook/react/blob/master/src/core/ReactPropTypes.js#L76-L91
http://facebook.github.io/react/docs/reusable-components.html#prop-validation
Note that you may want to put the logic of associating data between stores in the stores themselves. So your ArticleStore might waitFor() the UserStore, and include the relevant Users with every Article record it provides through getArticles(). Doing this in your views sounds like pushing logic into the view layer, which is a practice you should avoid whenever possible.
You might also be tempted to use transferPropsTo(), and many people like doing this, but I prefer to keep everything explicit for readability and thus maintainability.
FWIW, my understanding is that David Nolen takes a similar approach with his Om framework (which is somewhat Flux-compatible) with a single entry point of data on the root node -- the equivalent in Flux would be to only have one controller-view listening to all stores. This is made efficient by using shouldComponentUpdate() and immutable data structures that can be compared by reference, with ===. For immutable data structures, checkout David's mori or Facebook's immutable-js. My limited knowledge of Om primarily comes from The Future of JavaScript MVC Frameworks
The approach at which I arrived is having each components receive its data (not IDs) as a prop. If some nested component needs a related entity, it's up to the parent component to retrieve it.
In our example, Article should have an article prop which is an object (presumably retrieved by ArticleList or ArticlePage).
Because Article also wants to render UserLink and UserAvatar for article's author, it will subscribe to UserStore and keep author: UserStore.get(article.authorId) in its state. It will then render UserLink and UserAvatar with this this.state.author. If they wish to pass it down further, they can. No child components will need to retrieve this user again.
To reiterate:
No component ever receives ID as a prop; all components receive their respective objects.
If child components needs an entity, it's parent's responsibility to retrieve it and pass as a prop.
This solves my problem quite nicely. Code example rewritten to use this approach:
var Article = React.createClass({
mixins: [createStoreMixin(UserStore)],
propTypes: {
article: PropTypes.object.isRequired
},
getStateFromStores() {
return {
author: UserStore.get(this.props.article.authorId);
}
},
render() {
var article = this.props.article,
author = this.state.author;
return (
<div>
<UserLink user={author}>
<UserAvatar user={author} />
</UserLink>
<h1>{article.title}</h1>
<p>{article.text}</p>
<p>Read more by <UserLink user={author} />.</p>
</div>
)
}
});
var UserAvatar = React.createClass({
propTypes: {
user: PropTypes.object.isRequired
},
render() {
var user = this.props.user;
return (
<img src={user.thumbnailUrl} />
)
}
});
var UserLink = React.createClass({
propTypes: {
user: PropTypes.object.isRequired
},
render() {
var user = this.props.user;
return (
<Link to='user' params={{ userId: this.props.user.id }}>
{this.props.children || user.name}
</Link>
)
}
});
This keeps innermost components stupid but doesn't force us to complicate the hell out of top level components.
My solution is much simpler. Every component that has its own state is allowed to talk and listen to stores. These are very controller-like components. Deeper nested components that don't maintain state but just render stuff aren't allowed. They only receive props for pure rendering, very view-like.
This way everything flows from stateful components into stateless components. Keeping the statefuls count low.
In your case, Article would be stateful and therefore talks to the stores and UserLink etc. would only render so it would receive article.user as prop.
The problems described in your 2 philosophies are common to any single page application.
They are discussed briefly in this video: https://www.youtube.com/watch?v=IrgHurBjQbg and Relay ( https://facebook.github.io/relay ) was developed by Facebook to overcome the tradeoff that you describe.
Relay's approach is very data centric. It is an answer to the question "How do I get just the needed data for each components in this view in one query to the server?" And at the same time Relay makes sure that you have little coupling across the code when a component used in multiple views.
If Relay is not an option, "All entity components read their own data" seems a better approach to me for the situation you describe.
I think the misconception in Flux is what a store is. The concept of store exist no to be the place where a model or a collection of objects are kept. Stores are temporary places where your application put the data before the view is rendered. The real reason they exist is to solve the problem of dependencies across the data that goes in different stores.
What Flux is not specifying is how a store relate to the concept of models and collection of objects (a la Backbone).
In that sense some people are actually making a flux store a place where to put collection of objects of a specific type that is not flush for the whole time the user keeps the browser open but, as I understand flux, that is not what a store is supposed to be.
The solution is to have another layer where you where the entities necessary to render your view (and potentially more) are stored and kept updated. If you this layer that abstract models and collections, it is not a problem if you the subcomponents have to query again to get their own data.

Idiomatic way to listen to model changes

I'm playing with React for the first time and I think I really like it. I've implemented (large parts of) the board game Go with it and so far, but I've run into something strange that I don't know how to approach in the idiomatic React way. Basically, I've got a model--the board game--implemented as its own class Board. It exposes only it's constructor, and methods play(i,j) and pass. It handles all of the game logic and updates its own internal state appropriately. It has no reference to anything related to a view/component. I've got a React Component called BoardView which maintains a reference to an instance of a Board. I've also got a Component called AlertView that displays messages about the game state (illegal moves and such) when appropriate.
Everything works well now, and I like the separation of concerns between the Board class and its views. However, the way I have my Board class communicate its changes to the views is unusual, and I feel that it is inconsistent with other React code. Basically, I abuse jQuery's event system to allow me to trigger arbitrary events like ["update", "atari", "suicide"]. In this scheme, the Component has an onClick listener that calls Board.play, which triggers 0 to many events on the Board instance. The Component listens for an "update" event, and calls this.setState, which will force it to re-render(), putting the view into a state that correctly depicts the game. The AlertView listens for the "atari" and "suicide" events on the same board instance and similarly calls this.setState, which triggers another render().
Should I cut out the jQuery events? If so, what's the best way of doing this?
All code is available here and you can play with the app here.
Edit:
For posterity's sake, this question was asked at commit 3f600c.
I'm not sure if this is idiomatic React, but from the React tutorial, the onSubmit handler is passed from the parent to the children as a props.
In your case that would mean to pass the onPlay handler from BoardView to BoardIntersection like this:
var BoardView = React.createClass({
getInitialState: function() {
return {"board": this.props.board}
},
playHandler: function(i, j) {
this.props.board.play(i, j)
},
render: function() {
...
intersections.push(BoardIntersection({
color: this.state.board.board[i][j],
row: i,
col: j,
onPlay: this.playHandler
}));
...
}
})
and BoardIntersection will call onPlay as needed:
var BoardIntersection = React.createClass({
handleClick: function() {
this.props.onPlay(this.props.row, this.props.col);
},
})
tungd's comments pointed me in the right direction, but I decided to answer my own question for a more complete answer.
I ended up removing all of the custom events being fired on the model. I found the following snippet from the React docs to be especially helpful:
A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via props. The stateful component encapsulates all of the interaction logic, while the stateless components take care of rendering data in a declarative way.
Instead of firing events like "atari" and "suicide" on the model, I just set boolean properties on the model in_atari and attempted_suicide. Now, only one "parent" Component in my application has state. It renders all sub-components via declarative props. The AlertView is one such sub-component whose render method now checks the new boolean flags to render the appropriate text. The main parent Component passes a handler to its sub-components that updates the component state (and subsequently forces a re-render).
In the relevant commit, I've named the parent component ContainerView.

Categories