Async Client Side Rendering React components using Dust JS Templates - reactjs

I'm having a dust helper called {#component id="compId" path="path/to/react/component"}.
About React component:
The react component makes an ajax call in componentWillMount and calls forceUpdate.
psueodcode:
React.createClass(){
getInitialState : function() {
this.setState({response : "nothing"});
}
var response = "nothing";
componenetWillMount : function(){
//1.performs ajax call
//2.set state in ajax callback this.setState({response : "success"});
//3. calls this.forceUpdate()
},
render : function() {
//returns response text
}
}
About Dust Helper
component is added to dust helper and chunk is written with response from react.
dust.helpers.component = function(chunk, context, bodies, param) {
return chunk.map(function(chunk) {
var domElement = document.getElementById(param.id);
if (!domElement) {
console.log("Document does not present for " + param.id);
domElement = document.createElement(param.id);
}
require([param.path], function(widgetModule) {
console.log("Rendering " + param.path);
var widget = React.createFactory(widgetModule);
React.render(widget(null), domElement, function() {
chunk.end(domElement.innerHTML);
});
});
});
};
Response In Browser:
I'm able to see the response as "nothing" instead of "success". The issue is The render callback is called after the template is rendered on the browser hence the ajax response is not updated.
How would you make dust to listen for changes to a div after the page is rendered? Is it possible in dust, similar to how react finds diff between dom and virtual dom and decides to re-render. I'm using react 0.13.3 and dust 2.7.1

Related

globally access data across all pages in REACT js

In header react component class have defined function Search
Search: function()
{
var author=$('#author').val();
var bookname=$('#title').val();
var isbn=$('#isbn').val();
var keywords=$('#keywords').val();
var publisher=$('#publisher').val();
var url=VDOMAIN+"/aodrbooks.php?action=srch&author="+author+"&isbn="+isbn+"&publisher="+publisher+"&name="+bookname+"&price=''";
$.ajax({url:url,
success: function(result){
resp = JSON.parse(result); //this data is an array
if(resp['count']==1){
location.href=VDOMAIN+"odrbooks?pagename=bkd&bid="+resp['results'][0]['bookid']+"&bkname="+resp['results'][0]['name'];
}
else{
location.href=VDOMAIN+"odrbooks?pagename=cat&type=&author="+author+"&bkname="+bookname;
}
}
});
},
here how do i make my resp data globally such thAT I can access it in any page in any component?
I tried using this.state.data=resp ,this.props.data=resp and this.setState({data:resp})
Use state container for that purpose. The most popular one right now is https://github.com/reactjs/react-redux
You can dispatch an action and store successful response in Your reducer which represents Your application state.
Then connect that reducer to the component that needs resp data and use it through props.

Make AJAX request when the property was changed

I would like to have a component, which get the property from parent component and make an AJAX request, based on this propery. The parent component can change this property and my child component must get another one AJAX request.
Here is my code, but I am not sure it is optimal and even correct:
<News source={this.state.currentSource} />
Component:
var News = React.createClass({
propTypes: {
source: React.PropTypes.string
},
getInitialState: function() {
return {
entities: []
};
},
componentWillReceiveProps(nextProps) {
var url = 'http://localhost:3000/api/sources/' + nextProps.source + '/news';
this.serverRequest = $.get(url, function(result) {
this.setState({
entities: result
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
// ...
}});
module.exports = News;
Does componentWillReceiveProps is a good place for this code?
componentWillReceiveProps will work just fine, one thing I would do differently is if <News/> is getting its props from a parent component then a good pattern to follow is to have the parent component actually handle the api calls and pass down the results as props. This way your News component really only has to deal with rendering content based on its props vs rendering and managing state.
I can only see limited portion of your App so that might not fit your use case but here is a great article on doing that type of differentiation between smart and dumb components.
http://jaketrent.com/post/smart-dumb-components-react/

I can't render my component

I need help with React.js.
I have a React Component called ContactsApp, this component call the method componentDidMount() that containts an Ajax Call with recieve props.
componentDidMount(){
jQuery("document").ready(function(){
jQuery("#sent-btn").click(function(ev){
var data_json = {};
data_json['word'] = jQuery("#word").val();
data_json['route'] = jQuery("#route").val();
console.log(data_json['word']);
console.log(data_json['route']);
$.ajax({
type: 'post',
url: 'logic',
data: JSON.stringify(data_json),
contentType: "application/json; charset=utf-8",
traditional: true,
}).then(function(data){
console.log(data);
var list = []
for (var i = 0; i < data.Data.length; i++) {
list.push({"name":data.Data[i].File , "email":data.Data[i].Quantity})
}
this.setState({contacts:list});
}.bind(this))
});
});
}
But my problem is these errors that the web console response for me.
1) Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
2) Uncaught TypeError: this.setState is not a function
The class is:
- Container
- ContactsApp
- SearchBar
- ContactList
- ContactItem
Finally I call render method with:
render(<ContactsAppContainer />, document.getElementById('forms'));
I have seen that someone people uses React History, but Cassio Zen don't use this and I think I'm okay.
My import libraries:
import React, { Component, PropTypes } from 'react';
import { render } from 'react-dom';
import 'whatwg-fetch';
import jQuery from 'jquery';
I have app with Go that contains service 'login' for ajax call.
I was using a example based on React component by Cassio Zen.
Sorry my english, I'm learning this, please help me.
You're rebinding the context for this inside your AJAX callback, but you've already lost the component's context as soon as you enter the jQuery ready and click callbacks.
It seems like you're using ES6, so the simple fix would be to use arrow functions instead.
componentDidMount() {
jQuery("document").ready(() => {
jQuery("#sent-btn").click(ev => {
// ...
$.ajax({
// ....
}).then(data => {
// ...
this.setState({contacts:list});
});
});
});
}
However it looks like you've got bigger problems with the other error. I'm not exactly sure what's causing it, but I'm confident that all the while you use jQuery inside your React components, you'll be making everything difficult for yourself.
Honestly, I'd go back a few steps and try and get a better sense of how to use event handlers to listen for button clicks and input changes with React, not with jQuery. Then remove the jQuery dependency from your application and switch the DOM code over to use React and use switch the AJAX code over to using fetch.

How to force React to accept server markup

On the first load I render my markup completely filled with data on the server.
This works well and the client sees it as a static html till the client-side React takes over. But this client-side code throws the markup away.
I stopped React from requesting the data per AJAX on the first load (because it's already there), only if I navigate client-side with a react-router <Link> to it.
But I can't force it to simply let the markup be, till the user interacts with it.
You can use dangerouslySetInnerHTML attribute. For example, create a new component wrapper like this and set html property:
export default React.createClass({
render: function() {
return <div dangerouslySetInnerHTML={ this.createMarkup() }>
</div>;
},
componentDidMount: function() {
// attach some events
},
componentWillUnmount: function() {
// detach some events
},
shouldComponentUpdate: function() {
return false;
},
createMarkup: function() {
return {
__html: this.props.html
};
}
});

SetState fails in callback (via ComponentWillMount), on server only

I need to render React components on the server for SEO. My component fetches data in ComponentWillMount, based on the query parameters - but on the server (Node 4.0.0), SetState fails in the request's callback. The error can be reproduced with a simpler setTimeout too, as in the code example below.
I have found numerous discussion on the web relating to complications between React and server-side rendering. I'm working on two work-around approaches:
removing all ajax requests from the server, instead rendering the result of the request directly into a global variable embedded in the first-serve HTML
moving the ajax request prior to initialization of the React components, on the server only (the request would still have to live in ComponentWillMount (or ComponentDidMount) for the client version.
Please let me know if there is an alternative or recommended approach instead.
var React = require('react');
// Reproduced in React 0.13.3 and 0.14.0-beta1
var ReactDOMServer = require("react-dom/server");
var A = React.createClass({
componentWillMount: function() {
var _this = this;
// for example an ajax call to fetch data based on request parameters:
setTimeout(function(err, res) {
// state is set based on results
_this.setState({ a: 1 });
}, 100);
},
render: function() {
return React.createElement('div', null);
}
});
ReactDOMServer.renderToString(React.createElement(A, null));
Error:
$ node index.js
/app/node_modules/react/lib/getActiveElement.js:25
return document.body;
^
ReferenceError: document is not defined
at getActiveElement (/app/node_modules/react/lib/getActiveElement.js:25:12)
at ReactReconcileTransaction.ReactInputSelection.getSelectionInformation (/app/node_modules/react/lib/ReactInputSelection.js:38:23)
at ReactReconcileTransaction.Mixin.initializeAll (/app/node_modules/react/lib/Transaction.js:168:75)
at ReactReconcileTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:135:12)
at ReactUpdatesFlushTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:136:20)
at ReactUpdatesFlushTransaction.assign.perform (/app/node_modules/react/lib/ReactUpdates.js:86:38)
at Object.flushBatchedUpdates (/app/node_modules/react/lib/ReactUpdates.js:147:19)
at Object.wrapper [as flushBatchedUpdates] (/app/node_modules/react/lib/ReactPerf.js:66:21)
at ReactDefaultBatchingStrategyTransaction.Mixin.closeAll (/app/node_modules/react/lib/Transaction.js:202:25)
at ReactDefaultBatchingStrategyTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:149:16)
Issue opened at https://github.com/facebook/react/issues/4873
Try moving the setState function in another method:
var React = require('react');
// Reproduced in React 0.13.3 and 0.14.0-beta1
var ReactDOMServer = require("react-dom/server");
var A = React.createClass({
stateChange: function( obj ){
setTimeout( this.setState( obj ), 100 );
},
componentWillMount: function() {
this.stateChange( {a: 1} );
},
render: function() {
console.log( this.state.a )
return React.createElement('div', null);
}
});
ReactDOMServer.renderToString(React.createElement(A, null));

Resources