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
};
}
});
Related
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/
Note: I'm using the beautiful library react-rails though it should not impact the answer as far as I understand my problem.
I have a <Component /> which loads a <Map />, which implies client-side rendering as it doesn't make sense on the server side (at least the lib I'm using doesn't do that).
So instead, I want to display an image before the clientside is ready, to apply the Skeuomorphism principle.
Basically, this means I have:
var Component = React.createClass({
render: function() {
var content;
if (this.state.clientSideReady) { // How can I change my component state here?
content = <Map />
} else {
content = <PlaceholderImage />
}
return (<div>{content}</div>)
}
});
From my current understanding, componentDidMount is called on the server-side, when the template string is generated. How can I know the component actually mounted on the clientside so I can replace my image by the actual map?
My mistake. As from this answer componentDidMount isn't called on the server-side.
So I can go:
var Component = React.createClass({
getInitialState: function() {
return {
clientSideReady: false
}
},
componentDidMount: function() {
this.setState({
clientSideReady: true
});
},
render: function() {
var content;
if (this.state.clientSideReady) {
content = <Map />
} else {
content = <PlaceholderImage />
}
return (<div>{content}</div>)
}
});
Hitting a roadbump, I’m seeking some help. I'm starting to move more of my "state" pojo's out of my React components, due to for example being unsure how how my pojo’s setter methods should be utilized now (one may want setter methods to validate, etc.). I now find myself either defying React docs' warning to NEVER touch this.state directly or moving most code except rendering – including state - outside of the React component into my own js variables/objects (and holding a reference to the rendered object then using forceUpdate() to rerender). What is the recommended way to freely use whatever plain old js data/model objects I want, including with setter methods?
This barebones example, where I’m wanting a form-backing data object, demonstrates this difference I’m facing: http://jsfiddle.net/jL0rf0ed/ vs. http://jsfiddle.net/rzuswg9x/. Also pasted the code for the first below.
At the very least, I have this specific question: following a custom/manual update of this.state, does a this.setState(this.state) line, which would be from within the React component, and a component.forceUpdate() line, which would likely be from outside the React component, work just as fast and correctly as the standard this.setState({someKey: someValue})?
Thanks.
//props: dataObj, handleInputChange
test.ComponentA = React.createClass({
getInitialState: function() {
return {
age: 21,
email: 'a#b.com', //TODO make members private
setEmail: function(val) { //TODO utilize this
this.email = val;
if(val.indexOf('#') == -1) {
//TODO set or report an error
}
}
}
},
handleInputChange: function(e) {
this.state[e.target.name]=e.target.value; //defying the "NEVER touch this.state" warning (but it appears to work fine)!
this.setState(this.state); //and then this strange line
},
render: function() {
return (
<div>
<input type='text' name='age' onChange={this.handleInputChange} value={this.state.age}></input>
<input type='text' name='email' onChange={this.handleInputChange} value={this.state.email}></input>
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
});
React.render(<test.ComponentA />, document.body);
For your code example in your pasted snippet, you can do the following.
handleInputChange: function(e) {
var updates = {};
updates[e.target.name] = e.target.value;
this.setState(updates);
},
In your second example, you should never call forceUpdate or setState from outside the component itself. The correct way would be for the state to be contained in whatever renders your component and pass in the data as props.
Usually this means you have a wrapper component.
var RootComponent = React.createClass({
getInitialState: ...
onInputChange: function() {
this.setState({yourKey: yourValue});
},
render: function() {
return <SubComponent yourKey={this.state.yourKey} onInputChange={this.onInputChange} />;
}
};
In your case, I would recommend creating this wrapper component. Another solution is just to rerender the same component into the same DOM node.
test.handleInputChange = function(e) {
// update test.formPojo1 here
React.render(<test.ComponentA dataObj={test.formPojo1} handleInputChange={...} />);
}
Because it is the same component class and DOM node, React will treat it as an update.
Stores
Facebook uses the concept of a Store in their Flux architecture.
Stores are a very targeted POJO. And I find that it is pretty simple to use the Store metaphors without the using the entirety of Flux.
Sample Store
This is a Store that I pulled out of one of our production React apps:
ChatMessageStore = {
chatMessages: [],
callbacks: [],
getAll: function() {
return this.chatMessages;
},
init: function() {
this.chatMessages = window.chat_messages.slice();
return this.emitChange();
},
create: function(message) {
this.chatMessages.push(message);
return this.emitChange();
},
emitChange: function() {
return this.callbacks.forEach(callback, function() {
return callback();
});
},
addChangeListener: function(callback) {
return this.callbacks.push(callback);
},
removeChangeListener: function(callback) {
return this.callbacks = _.without(this.callbacks, callback);
}
};
Hooking it up to a React Component
In your component you can now query the store for its data:
var Chat = React.createClass({
componentWillMount: function() {
return ChatMessageStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
return ChatMessageStore.removeChangeListener(this._onChange);
},
getInitialState: function() {
return this.getMessageState();
},
getMessageState: function() {
return {
messages: ChatMessageStore.getAll()
};
}
});
The Component registers a callback with the Store, which is fired on every change, updating the component and obeying the law of "don't modify state."
Is it possible to create an Angular app inside a react component?
Something like:
React.createClass({
render: function() {
return <div ng-app>...</div>;
}
});
This is completely backwards, but will only have to be a temporary solution. I'm assuming I could do something like the code above, and add the angular bootstrapping to the componentDidMount lifecycle method.
Has anyone successfully done this?
Thanks.
It would look something like this:
componentDidMount: function(){
this._angularEl = document.createElement("div");
this._angularEl.innerHTML = angularTemplatHTMLString;
this._angularEl.setAttribute("ng-app", "");
this.getDOMNode().appendChild(this._angularEl);
// bind angular to this._angularEl
},
componentWillUnmount: function(){
// unbind angular from this._angularEl
this.getDOMNode().removeChild(this._angularEl);
delete this._angularEl;
},
render: function(){
return <div></div>
}
You could either use this for each component, or create a function which returns a mixin.
function makeAngularMixin(template){
return { /* above code except for render */ }
}
or have a component which allows passing an angular template via props.
I was trying to add a click handler to my own child component. In react chrome extension I was able to see the click handler as well.
But the click itself didn't work - wondering what did I miss.
Sample Code:
...
render (
<MySampleComponent onClick={this.handler} />
);
...
MySampleComponent can take whichever props it wants; components don't automatically copy props to their children. If you want to be able to add an onClick handler to MySampleComponent, then you can support this in the definition of that component:
var MySampleComponent = React.createClass({
render: function() {
return <div onClick={this.props.onClick}>...</div>;
}
});
You can add the handler from the samecomponent or call it through props.
Below code looks for onClick param in props. If nothing is passed, then
it goes for default handler in the component(clickHandler).
var MySampleComponent = React.createClass({
clickHandler: function(){
// write your logic
},
render: function() {
return <div onClick={this.props.onClick || this.clickHandler}>...</div>;
}
});
and while using this in another component use it as below
...........
handler: function() {
// write your logic
},
render {
var self = this;
return (<MySampleComponent onClick={self.handler} />);
}
......