I'm relatively new to ReactJS and have been seduced by the ease of implementing server-side rendering to reduce "time to first tweet". I'm running a Node-Express-React stack which pre-renders the markup on the server using React's renderComponentToString.
It works fine when the components can be rendered synchronously, however I'm struggling when it comes to implementing ajax-populated components (however this applies to any asynchronous operation during the initialization of the component, e.g. websocket).
Take the example from the React website: http://facebook.github.io/react/tips/initial-ajax.html
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
It won't work on the server, since componentDidMount is never triggered when using renderComponentToString.
This simple case can be worked around by using the same HTTP request wrapper on the client and on the server (instead of using jQuery's $.get), and by pre-fetching the data before instantiating the component and passing it as a prop.
However, in an actual, complex app, asynchronous dependencies can get very complicated and pre-fetching doesn't really fit the descendant approach of building React structures.
How can I implement an asynchronous initialization pattern in React that can be rendered on the server without actually mounting anything (i.e. no DOM emulation a la PhantomJS, which is the whole point of using renderComponentToString) ?
I believe the most practical way to do this is to make an optional prop for the preloaded data, like so:
getInitialState: function() {
if (this.props.initialLastGist) {
var lastGist = this.props.initialLastGist;
return {
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
};
} else {
return {};
}
},
componentDidMount: function() {
if (!this.props.initialLastGist) {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
},
With a setup like this, the component can be rendered immediately if the preloaded data is present; otherwise the AJAX request will be sent while mounting.
Currently, server rendering is always synchronous and componentDidMount isn't called on the server because it usually involves DOM manipulation. Sorry I don't have a better solution here right now, but in general you want to minimize the number of HTTP requests to the server so it's worth thinking through your architecture so that you can collect all the data you need on the server.
You could take a look at the react-quickstart project, which uses react-async and ships with a server-rendering example. react-async provides a decorated renderComponentToString method that pre-fetches asynchronous state and renders it into the initial markup. However, you will need to specify asynchronous state separately from 'regular' state.
you can check out react-nexus (https://github.com/elierotenberg/react-nexus) which helps you declare all your async dependencies ahead of time.
but I realize that react-nexus is your own answer to this problem so you probably already found it !
Related
I'm still pretty new on React development, but I've already work on 3 big project using React+Redux and I see a pattern that I dislike a lot:
componentWillReceiveProps(nextProps) {
if (nextProps.params.type === TYPE_NEW_USER) {
this.modalUsername = this.props.showPopup( < NewUsernamePopup onClose = {::this.closeUsernamePopup
}
/>, USERNAME_POPUP_ID, true);
}
if (this.state.kind !== nextProps.kind || this.state.filter !== nextProps.filter || this.state.hashtags !== nextProps.hashtags) {
this.setState({
results: [],
loading: true,
kind: nextProps.kind,
filter: nextProps.filter,
hashtags: nextProps.hashtags
}, () => this.manageResults(nextProps.results, false));
} else {
this.manageResults(nextProps.results, true);
}
this.managePages(nextProps.paging);
}
I would like to avoid the ifs inside the componentWillReceiveProps. How do you handle it? We've analysed another project using Flux and callback registration. It looks like:
componentWillMount() {
EntityStore.on(EntityActions.ENTITIES_LOADED, this.getData.bind(this));
EntityActions.entitiesLoaded();
}
The first event is emitted by the component, but afterwards the store emits the event and the component updates. Additionally a single store keeps its state and do not duplicate async calls if it already has the content. I personally like to avoid the ifs, but I do NOT want to lose Redux (its community and tools).
How would you add the current logic (ifs) inside the componentWillReceiveProps outside the component? I would like to handle the logic in a service layer and not inside the component.
I would definitely appreciate to read your opinion around this, because I've been struggling to find a solutions that fits.
The redux approach is to put the logic into the actions/reducers.
So i don't know what your manageResults method does, but it is probably the piece of logic you want to move into a reducer so you won't need to call it from your component anymore.
So the kind,filter and hashtagsvariables should be updated from redux actions only.
tl;dr properly following redux best practices would eliminate some of these conditions, but I'd be more concerned about the overall design this snippet is revealing.
To address the individual lines:
if (nextProps.params.type === TYPE_NEW_USER) {
This looks like a redux action was passed to the component? If so, that's not great, only the reducers should care about action types.
this.modalUsername = this.props.showPopup(
The lifecycle hook componentWillReceiveProps is not the right place to initiate things like that, the resulting React component in an instance var also looks quite weird.
if (this.state.kind !== nextProps.kind || this.state.filter (etc.) ) {
If you have UI state in this component that is somehow dependant on the props coming from redux, these types of ifs are somewhat necessary, since you can't do it outside the component.
You are right to dislike this "pattern", which seems to reflect bad overall design. This component seems to be involved with "pages", "results", a username, and some ajax fetching with a loading flag. Can only speculate of course, but it seems like it's doing too much. The ajax request lifecycle should definitely be modelled in a reducer.
That said, the lifecycle hooks do often contain a bunch of ifs, since the reducers don't see routing and which components get mounted/unmounted, so that's where you have to react to changing props sometimes.
I would like to perform some initial data loading when my first route is rendered (for example, i want to load a list of news articles)
I made a component called News.js which renders the articles. The problem i'm experiencing with the FLUX model is where to load this initial data.
The service i have made to load the data is the following:
import request from 'superagent';
class NewsService {
fetchArticles(callback) {
request
.get('http://localhost/articles')
.accept('json')
.end(function(err, res){
console.log(err);
var result = JSON.parse(res.text);
callback(result);
})
}
}
export default new NewsService ();
This service has to be called somewhere. According to the ReactJS documentation i would perform this operation like this:
export default class News extends React.Component {
constructor() {
super();
this.state = {
_articles: []
}
}
componentDidMount() {
NewsService.fetchProjects(function(articles){
// load articles in the state
this.setState({_articles: _articles})
});
}
render() {
return (
<section>
<h1>Articles</h1>
<ul>
{this.state.articles.map((article) => {
<li>{article.title}</li>
})}
</ul>
</section>
)
}
}
Now my question is, isn't this against the flux principle? Shouldn't the data be called as an Action which then stores it's data in a store such as NewsStore?
Should an action be like the following:
var NewsActions = {
load: function() {
NewsService.fetchProjects(function(articles){
// store the articles in the NewsStore and dispatch afterwards
});
},
create: function(project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_CREATE,
project: project
});
},
update: function(id, project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_UPDATE,
id: id,
project: project
})
},
destroy: function() {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_DESTROY,
id: id
})
}
};
export default NewsActions;
In the Chat-app example of reactjs they provide an API call example. However this API call is called on the application start up (in app.js) which is not applicable in my scenario as i would like to use routings. Would i load the data in the router then? (I am using react-router)
Any feedback regarding this matter or improvements of this code is more than welcome.
EDIT
isn't this against the flux principle?
Maybe, maybe not. Seems like Flux is pretty flexible. From what I understand, it's more of a framework of principles rather than a strict "protocol". It's hard to say, but it appears that both example you've given will work. Like you said, according to the docs, they recommend fetching the data in componentDidMount:
componentDidMount: function() {
$.get(this.props.source, function(result) {
// ...
However, in your example, you're simply moving that API call into a service, which can then interact with the store/dispatcher, etc., in order to be utilized across the entire application.
So what you've done is moved a nice chunk of your application logic to, essentially, its own module (well, a module that is a part of your dispatchers). It appears that it will meet your needs: it can be used across your app, and it can be pulled out or plugged back in as you see fit. I don't see anything wrong with it. Could it be against some principle of Flux? Maybe, maybe not. I doubt it matters, though.
ORIGINAL
I'm not well-versed in Flux architecture, but looking at one of Facebook's examples in their GitHub repo (specifically, Line 24 of TodoMVC):
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Their example doesn't show how TodoStore interacts with the server, but it does look like for their initial state, they're simply querying the todos in the store, and then for changes, their listening for and emitting events.
So as far as getting the initial state, it looks like their example shows querying the store directly. Obviously since the time they made that example and now, there may have been changes, but it may be worth investigating some examples in the Flux repo to get an idea of how it was designed.
We want to build a production-ready social network website (Facebook or Instagram style). We plan to use Node on the server side and are looking for the best technology for rendering view components on the server side.
SEO friendliness is a must, so Angular doesn’t seem like a good fit (unless anyone can advocate for Prerender.io as a solid choice).
We also wish to support AJAX so that most of the rendering would be done on the server, but updates would be done on the client.
Having looked at React, it seems like a good choice, but there’s one thing I’m worried about - the fact that out of the box, you would need to load all data at the route level as opposed to the component level (since renderToString is synchronous - see the discussion here
I don't really understand what would be a robust alternative for server side rendering if we're passing on React.
If I understnd correctly, the basic way (which does allow async loading from within sub components) would be something like:
// The main page route
app.get('/',function(){
var html = renderBase();
renderHeader(html)
.then(
renderFeed(html)
).then(
renderFooter(html)
);
})
renderBase = function(){
return = "<html><body>..."
}
renderHeader = function(){
someIoFunction(function(){
// build HTML based on data
})
}
Seemingly using a template engine such as Jade might relieve us of some of the burden, but I can't seem to find anything on this "React vs. Templating engine" so-called issue, so probably I'm not seeing it correctly.
Thanks.
Basically the solutions come down to using a convention where each component has a static function which returns the data it needs, and it calls the function of the same name on the components it needs. This is roughly how it looks:
class CommentList {
static getData = (props) => {
return {
comments: api.getComments({page: props.page})
};
}
}
class CommentApp {
static getData = (props) => {
return {
user: api.getUser(),
stuff: CommentList.getData({page: props.commentPage})
};
}
render(){
return <CommentList comments={this.props.stuff.comments} />
}
}
Or you can figure out all of the data requirements at the top of the tree, but while simpler to start out, it's more fragile. This is what you do with templates.
I have been writing components in React.js recently. I have never had to use methods like componentWillMount and componentDidMount.
render is indispensable. getInitialState and other helper methods I wrote also come in handy. But not the two aforementioned lifecycle methods.
My current guess is that they are used for debugging? I can console.log out inside them:
componentWillMount: function() {
console.log('component currently mounting');
},
componentDidMount: function() {
console.log('component has mounted');
}
Are there any other uses?
componentDidMount is useful if you want to use some non-React JavaScript plugins. For example, there is a lack of a good date picker in React. Pickaday is beautiful and it just plain works out of the box. So my DateRangeInput component is now using Pickaday for the start and end date input and I hooked it up like so:
componentDidMount: function() {
new Pikaday({
field: React.findDOMNode(this.refs.start),
format: 'MM/DD/YYYY',
onSelect: this.onChangeStart
});
new Pikaday({
field: React.findDOMNode(this.refs.end),
format: 'MM/DD/YYYY',
onSelect: this.onChangeEnd
});
},
The DOM needs to be rendered for Pikaday to hook up to it and the componentDidMount hook lets you hook into that exact event.
componentWillMount is useful when you want to do something programatically right before the component mounts. An example in one codebase I'm working on is a mixin that has a bunch of code that would otherwise be duplicated in a number of different menu components. componentWillMount is used to set the state of one specific shared attribute. Another way componentWillMount could be used is to set a behaviour of the component branching by prop(s):
componentWillMount() {
let mode;
if (this.props.age > 70) {
mode = 'old';
} else if (this.props.age < 18) {
mode = 'young';
} else {
mode = 'middle';
}
this.setState({ mode });
}
componentDidMount only runs once and on the client side. This is important, especially if you're writing an isomorphic app (runs on both the client and server). You can use componentDidMount to perform tasks require you to have access to window or the DOM.
From Facebook's React Page
If you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests, perform those operations in this method.
componentWillMount has fewer use cases (I don't really use it), but it differs in that it runs both on the client and server side. You probably don't want to put event listeners or DOM manipulations here, since it will try to run on the server for no reason.
This is an example of an isomorphic web application that makes use of componentWillMount: https://github.com/coodoo/react-redux-isomorphic-example
However, I'm 99% certain that it runs the code inside componentWillMount for no reason on the server side (I think using componentDidMount to ensure it was only run client side would have made more sense) as the code which ensures that fetch promises are fulfilled before rendering the page is in server.js not inside the individual components.
There is discussion on per-component async fetching here: https://github.com/facebook/react/issues/1739 As far as I can tell, there is not a good use case for componentWillMount as far as isomorphism is concerned at least.
In my project which is a dashboarding tool, I have used componentDidMount().
On home page previously saved dashboards appear on the sidebar. I make an ajax call within componentDidMount() of component rendering Homepage, so as to fetch list of dashboards asynchronously after the page has been rendered.
Why React Life Cycle Methods?
Intend to use third-party (Ex D3.js) library with React Component
class Example extends React.component{
constructor(){
// init state
// will be called only once
}
componentWillMount(){
// will be called only once
// will not be triggered when re-rendering
// usually will fetch data that is needed in order
// to render properly from other API
}
shouldComponentUpdate(){
return false
// will not re-render itself after componentDidMount(){}
}
render(){
return (
<div id="chart"></div>
)
}
componentDidMount(){
d3.select(".chart")
.selectAll("p")
// d3.js ........
// d3.js ........
// Usually, this will trigger React to re-render itself,
// but this time will not because we have set
// shouldComponentUpdate to false
}
}
Why React wants to do this?
Since rendering DOM is an expensive operation, React uses the layer of virtual DOM to update only DOM / DOMs that is/are different from previous state.
I am currently working on a prototype application using the flux pattern commonly associated with ReactJS.
In the Facebook flux/chat example, there are two stores, ThreadStore and UnreadThreadStore. The latter presents a getAll method which reads the content of the former synchronously.
We have encountered a problem in that operations in our derived store would be too expensive to perform synchronously, and would ideally be delegated to an asynchronous process (web worker, server trip), and we are wondering how to go about solving this.
My co-worker suggests returning a promise from the getter i.e.
# MyView
componentDidMount: function () {
defaultState = { results: [] };
this.setState(defaultState);
DerivedStore.doExpensiveThing()
.then(this.setState);
}
I'm not entirely comfortable with this. It feels like a break with the pattern, as the view is the primary recipient of change, not the store. Here's an alternative avenue we've been exploring - in which the view mounting event dispatches a desire for the derived data to be refreshed (if required).
# DerivedStore
# =========================================================
state: {
derivedResults: []
status: empty <fresh|pending|empty>
},
handleViewAction: function (payload) {
if (payload.type === "refreshDerivedData") {
this.state.status = "pending"; # assume an async action has started
}
if (payload.type === "derivedDataIsRefreshed") {
this.state.status = "fresh"; # the async action has completed
}
this.state.derivedResults = payload.results || []
this.notify();
}
# MyAction
# =========================================================
MyAction = function (dispatcher) {
dispatcher.register(function (payload) {
switch (payload) {
case "refreshDerivedData":
doExpensiveCalculation()
.then(function(res) {
dispatcher.dispatch({
type: "derivedDataIsRefreshed",
results: res
})
})
);
}
});
};
# MyView
# =========================================================
MyView = React.createClass({
componentDidMount: function () {
if (DerivedStore.getState().status === "empty") {
Dispatcher.dispatch("refreshDerivedData");
}
},
getVisibility: function () {
return DerivedStore.getState().status === "pending" ? "is-visible" : ""
},
render: function () {
var state = DerivedStore.getState()
, cx = React.addons.classSet
, classes = cx({
"spinner-is-visible": state.status === "pending"
});
return <div {classes}>
<Spinner /> # only visible if "spinner-is-visible
<Results results={state.derivedResults}/> # only visible if not...
</div>;
}
});
# MyService
# =========================================================
# ensure derived data is invalidated by updates in it's source?
OriginalStore.addListener(function () {
setTimeout(function () {
dispatcher.dispatch({
type: "refreshDerivedData"
})
}, 0);
});
What I like about this approach is that the view treats the DerivedStore as it's view model, and views of this ilk are primarily interested in the freshness of their view model. What concerns me however is the potential for stores coming out of sync.
My question(s) then:
is the promise approach acceptable?
is the second approach better/worse? If so, why?
is there an existing "canonical" approach to this problem?
PS: sorry if there are any fundamental linting errors in this code, I've been working in Coffeescript for the last 3 months and it's destroyed my linting powers...
All async actions should be caused by the creation of an action. The completion of an async action should be signaled by the creation of another action. Stores may listen to these actions, and emit a change event.
In your component you listen to a DerivedStore for changes. An action can be created from anywhere, such as in your component or another store. The data is (eventually) derived, the store is updated, a change event is emitted, and your component(s) apply the event payload to state.
All in all, your component doesn't actually know if what's happening behind the scenes is sync or async. This is great because it allows you to make these performance changes behind the scenes without risk of breaking your components.
Pure stores usually only have one public function which gets the state of the store. In your components you should only call this in getInitialState, or better yet: have a mixin which does this and adds the change listener for you.
It sounds like a combination of the following discussions on github could help you.
store.getItem() which may require an async server call:
https://github.com/facebook/flux/issues/60
Managing amount of client-side data:
https://github.com/facebook/flux/issues/62
Essentially getting the store data is synchronous, the component could then tell the store to do the long running task but then forgets about it.
Once the task is completed in the store an action is created and the flow thing happens at which time the component can synchronously get the required information from the store.
Does that make sense?
If I was going to create an async process in the most Flux way possible, I would approach it much like an XHR request -- kick off the async process in either the Action Creator or the Store (whichever makes the most sense for the app) and then call a new Action Creator to dispatch a new action when the async process completes. This way, multiple stores can respond to the completed expensive async process, and the data flow is still emanating from an Action.
You could also just add a handler to your Store that gets called when a certain event is emited in your store
So lets say in your store you have a method:
Store = { ...
addUnreadDoneListener:function(callback){
this.on(SOME_EVENT_CONSTANT, callback);
},
...}
In your componentWillMount you can register to this "addUnreadDoneListener" with a function of your component, which then gets called everytime your store emits this certain event.
I personally do this aswell in my project. And I think its pretty easy to manage this way
Hope this helped.
EDIT: I forgot to mension... I use Eventemitter to do this.