Reactjs Render component dynamically based on a JSON config - javascript

I have a following config as JSON
var componentConfig = {
content: { type: "ContentContent", data: "content"},
new_content: { type: "ContentFormContent", data: "content"}
}
In react rendercomponent, is it possible to pass the component name dynamically to react render.
for e.g in this rendercomponent instead of putting the ContentFormContent directly is it possible to pass the data from json config and i can loop or something.
React.renderComponent(<ContentFormContent data={[componentConfig.new_content.data]} />, body);
SO i will have a list of pages in json config and based on the selection of particular nav i will render the component based on its 'type' from the json file

The JSX
<ContentFormContent data={[componentConfig.new_content.data]} />
simply compiles to
ContentFormContent({data: [componentConfig.new_content.data]})
so you can make that function call however you like. In this case, it's probably most convenient to make a list of all possible components and do something like
var allComponents = {
ContentContent: ContentContent,
ContentFormContent: ContentFormContent
};
// (later...)
React.renderComponent(allComponents[component.type]({data: component.data}), body);
if component is an element from your example array.

React.renderComponent() has been deprecated, to use React.render()
https://facebook.github.io/react/blog/2014/10/28/react-v0.12.html#deprecations
You may do something like:
var loadReactModule = function ($root, type, data) {
var ContentContent= React.createClass({
render: function () {
return (
<input type="text" placeholder="My Module-Input"/>
);
}
});
var ContentFormContent= React.createClass({
render: function () {
return (
<form></form>
);
}
});
var allComponents = {
ContentContent: ContentContent,
ContentFormContent: ContentFormContent
};
if (type in allComponents) {
$root.each(function (index, rootElement) {
React.render(React.createElement(allComponents[type]), {data:data}, rootElement);
});
}
};

Related

React component get request being made in one or clicks late

This one is kind of hard to explain, but basically when a click on a component, I make a get request for some data for another component. This however is only made after a couple of clicks.
I should probably also admit that I am not 100% sure if the place I am making the request is even correct, so if that's the case please let me know how I can get that fixed. Here's the code:
var ChangeLogData = React.createClass({
getInitialState: function () {
return {
content: {},
}
},
render: function () {
var _this=this;
$.get(this.props.source, function (data) {
var log = $.parseJSON(data);
_this.state.content = log;
}.bind(this));
return (
<div>
{_this.state.content[0]}
</div>
);
}
});
window.ChangeLog = React.createClass({
render: function () {
return (
<div>
<ChangeLogData name={this.props.params.name}
source={currentUrl + "/changelog/" +
this.props.params.name}/>
</div>
);
}
});
Edit: I should also probably add that it seems that most people recommend doing http requests on componentWillMount, but if I do that, the request only works once.
Edit 2: Here is the code of where the event is being called:
var AboutItem = React.createClass({
render: function () {
return (
<ListGroup>
{this.props.list.map(function (listValue,key) {
var link = currentUrl + "/changelog/" + listValue.split(' ')[0];
return <ListGroupItem key={key} className="module"
bsStyle="warning">
{listValue}
</ListGroupItem>
})}
</ListGroup>
);
}
});
I guess the idea is, the user will click on an item (that is dynamically generated), and when the item is clicked, it will send to the ChangeLog component the data in which it has to do the get request. So where exactly would I put my event handler?
I think the problem is that it's not being called correctly, as jquery is async...
var jqxhr = $.get( "example.php", function() {
alert( "success" );
})
.done(function() {
// PUT YOUR CALLBACK CODE HERE WITH THE RESULTS
})
.fail(function() {
alert( "error" );
})
.always(function() {
alert( "finished" );
});
And update the state in the .done()
You should not be making requests in your render method. You should also not be directly modifying state through this.state but instead use this.setState(). You also don't seem to be adding any onClick handlers.
You can do something like the following to trigger requests onClick:
var ChangeLogData = React.createClass({
getInitialState: function () {
return {
content: {},
}
},
handleClick: function() {
$.get(this.props.source, function (data) {
var log = $.parseJSON(data);
this.setState( { content: log } );
}.bind(this));
}
render: function () {
return (
<div onClick = { this._handleClick } >
{_this.state.content[0]}
</div>
);
}
If you want to do it on component mount you can put into componentDidMount() and call setState when you retrieve your data. This will cause the component to re-render with content
First: the get request is async, so by the time you get the response back the DOM is already rendered.
Second: Never update state inside the render method, if it works and you don't get an error message you most likely will create an infinite loop, render => updateState => render => udpateState...
you have multiple options, you can have the get request inside the function called after onClick (not shown in your code), and then update state and pass data as props. In this case you would be making a new get request every single time there's a click event. If you dont need a new get request on every click look into react lifecycle methods, in particular componentDidMount, which is basically executed after the react component is mounted, then you can do a get request there and update the state
componentDidMount: function() {
$.get(this.props.source, function (data) {
var log = $.parseJSON(data);
this.setState( { content: log } );
}.bind(this));
},
I can't see from your code what component should be clicked in order to trigger the request, but as far as could see, you should take the request out of the render() method. This method is called every time state/props change, so it might make your code make the request multiple times.
Another thing is that you should always mutate your state by calling this.setState() method, so in your case it would be _this.setState({ content: log }).
Now, if you change the name prop every time another component is clicked, you should do something like:
var ChangeLogData = React.createClass({
getInitialState: function () {
return {
content: {},
}
},
componentWillMount: function () {
this.getLog(this.props.source);
},
componentWillReceiveProps: function (nextProps) {
this.getLog(nextProps.source);
},
getLog: function (source) {
var _this = this;
$.get(source, function (data) {
var log = $.parseJSON(data);
_this.setState({
content: log
});
}.bind(this));
}
render: function () {
return (
<div>
{this.state.content[0]}
</div>
);
}
First you can extract the process to create the request call from the render() method into its own method, in this case, getLog(). Then, with two new methods from the React lifecycle, you can call getLog() when the component will be mounted and whenever new props come in from the parents components.

React component not rendered

I have a file called App.js which is my application's main component.
I placed a new component in the result returned by that file's render method:
return (
<div>
<AjaxReq />
//many other components
</div>
);
Where AjaxReq is the following method:
'use strict';
var AjaxReq = React.createClass({
loadDoc: function() {
$.ajax({
url: "someUrl",
context: document.body,
success: function(){
$(this).addClass("done");
}
});
},
render: function() {
return (<div>
<p onClick={this.loadDoc}>
Click this to make AJAX request.
</p>
</div>);
}
});
module.exports = AjaxReq;
Unfortunately, this component is not rendered at all in the page.
Are there any issues with my code?
I don't think that this snippet $(this).addClass("done"); does what you might intend to do.
In that context, this refers to the React Component (Virtual DOM), not the actual element in the DOM.
The only way to access a React Component instance outside of React is by storing the return value of ReactDOM.render.
Also by any chance,have you forgotten to import React (var React = require('react') ) into your AjaxReq module?
Like phpcoderx said, not importing React could be causing nothing to render. To add the CSS class like you are trying to do, you would want to do something more like the following (though I don't think this would affect the lack of initial rendering issue you are seeing).
'use strict';
var AjaxReq = React.createClass({
loadDoc: function() {
$.ajax({
url: "someUrl",
context: document.body,
success: function(){
this.setState({ isLoaded: true });
}
});
},
getInitialState: function() {
return { isLoaded: false };
},
render: function() {
var classname = this.state.isLoaded ? 'done' : '';
return (<div className={classname}>
<p onClick={this.loadDoc}>
Click this to make AJAX request.
</p>
</div>);
};
});
module.exports = AjaxReq;

How do I pull items from MongoDB and update them when a new one is entered?

I am trying to build a simple blog with React, Express, MongoDB and Node. But I am still confused on (1) how to correctly make the ajax request to my database and how do I set state and (2) how to properly update the state.
I've tried setting getInitialState by making an AJAX request, but it won't work. Also I don't know if that is best practice. Also, once someone adds a new post, where am I supposed to place the POST and then how do I properly update state?
var React = require('react');
var List = React.createClass({
render: function() {
return (
<div>
<h2>{this.props.postbody}</h2>
</div>
)
}
})
// I'll break this up into smaller components later, but for now I just want
// to know where to put my database entries into the posts array.
// The only field in MongoDB right now is postbody.
var Home = React.createClass({
getInitialState: function() {
return {
posts: []
}
},
handleClick: function() {
$.ajax({
type: 'GET',
url: '/api/blogPosts',
success: function(data) {
this.setState = data;
console.log(this.setState);
}
})
},
render: function() {
return (
<div>
{this.state.posts.map(function(post) {
return (
<List postbody={post.postbody}></List>
)
})}
</div>
)
}
})
setState is a function, not a property to be set on this. You should do this.setState(data)

Why am I unable to access my Mongo collection in a React component?

Using reactjs:react as the official react package isn't installing correctly for Windows yet.
Just trying to get to grips with React and getting pretty frustrated by what seem to be small things. For some reason I can't actually query any of my Mongo collections via my React components - the basic Mongo queries in the Chrome console work as expected...
var ExampleComponent = ReactMeteor.createClass({
getInitialState: function() {
return {data: []};
},
//didn't think the following was necessary, but tried it to no avail:
startMeteorSubscriptions: function() {
Meteor.subscribe('exampleCollection');
},
componentDidMount: function() {
var collection = ExampleCollection.find().fetch();
console.log(collection); //Empty Array []
console.log(ExampleCollection); //Mongo Collection
console.log(ExampleCollection.find()); //Cursor
console.log(ExampleCollection.find().fetch()); //[]?? wtf?
this.setState({data: collection});
},
render: function() {
return (
<div data={this.state.data}>
Hello World?
</div>
);
}
});
Meteor.startup(function() {
React.render(<ExampleComponent />, document.getElementById('root'));
})
So what's going on here? Any help would be appreciated, I'm not finding as many resources about doing the basics with React and Meteor that I had hoped.
In reactjs:react, you need to implement a method: getMeteorState()
This is what sets your data to be available in your component when render() is called. You still should implement startMeteorSubscriptions if you're doing pub/sub with your data (which you did correctly).
For example:
var ExampleComponent = ReactMeteor.createClass({
// Methods specific to ReactMeteor
startMeteorSubscriptions: function() {
Meteor.subscribe('exampleCollection');
},
getMeteorState: function() {
return {
data: ExampleCollection.find().fetch()
};
},
// React Methods
getInitialState: function() {
return {};
},
render: function() {
var data = this.state.data;
return (
<div>
{/* Present your data here */}
</div>
);
}
});

react.js - Deep Object in state with async data does not work

I've just figured out that object in React's state that have multiple children cannot be rendered easily.
In my example I have component which speaks with third-party API through AJAX:
var Component = React.createClass({
getInitialState: function () {
return {data: {}};
},
loadTrackData: function () {
api.getDataById(1566285, function (data) {
this.setState({data: data});
}.bind(this));
},
componentDidMount: function () {
this.loadTrackData();
},
render: function () {
return (
<div>
<h2>{this.state.data.metadata.title}</h2>
</div>
);
}
});
The problem is that {this.state.data.metadata} renders fine..
But {this.state.data.metadata.title} throws error Uncaught TypeError: Cannot read property 'title' of undefined!
What is the proper way to deal with such async data?
I always like to add the loading spinner or indicator if the page has async operation. I would do this
var Component = React.createClass({
getInitialState: function () {
return {data: null};
},
loadTrackData: function () {
api.getDataById(1566285, function (data) {
this.setState({data: data});
}.bind(this));
},
componentDidMount: function () {
this.loadTrackData();
},
render: function () {
var content = this.state.data ? <h2>{this.state.data.metadata.title}</h2> : <LoadingIndicator />;
return (
<div>
{content}
</div>
);
}
});
with the loading indicator basically it improve the user experience and won't get much of unwanted surprise. u can create your own loading indicator component with lots of choices here http://loading.io/
this.state.data.metadata is undefined until loading occurs. Accessing any property on undefined gives you a TypeError. This is not specific to React—it's just how JavaScript object references work.
I suggest you use { data: null } in initial state and return something else from render with a condition like if (!this.state.data).

Categories

Resources