Display single item in Meteor + Flowrouter + React - javascript

Am a little bit stuck with Meteor, Flowrouter and React when it comes to display single items. Have been trying several solutions but don't know how to transfer the ID to my React class and can't really find any information. A push in the right direction to help understand how to do this correctly would be very appreciated
My route looks like this
FlowRouter.route('/video/:_id', {
name: "video",
action(pathParams, queryParams) {
console.log("Got the postId from the URL:", pathParams._id);
console.log("Query parameters:", queryParams);
ReactLayout.render(App, {
content: <Play />
});
}
});
So this help me get the ID and then in my Play React Class i have this code
Play = React.createClass({
renderPlay() {
return Videos.findOne(FlowRouter.getParam("_id"));
},
render() {
return (
<div className="container">
{this.renderPlay()}
</div>
);
}
});
But what i really would like to do is to pass the information to my React Clip class and also put values in variables.
Clip = React.createClass({
getDefaultProps: function () {
return {
src : 'https://www.youtube.com/embed/',
width: 1600,
height: 900
}
},
propTypes: {
video: React.PropTypes.object.isRequired
},
render() {
return (
<iframe
src={this.props.src + this.props.video.videoId} frameBorder={ 0 }>
</iframe>
);
}
});
To do this i would need to include something like this in my Play class.
renderVideo() {
// Get tasks from this.data.tasks
return this.data.videos.map((video) => {
return <Clip key={video._id} video={video} />;
});
},
Would love to understand how to do this correctly and it would really be a big step in the right direction to to understand this stack.
Tutorials and guides covering Meteor + React + kadira:Flowrouter + kadira:ReactLayout that handle more than just single page apps are welcome.

Related

How do I get a different result with the same state call in React Native?

My apologies for the confusing wording of the question. Basically when I call state from here:
this.state = {
newdiscoverPlanet: [
'sunp',
'twop',
'bluep',
'purplep',
'bluepurplep',
'redp',
'orangep'
],
};
_getRandomPlanet(){
var planetItem = this.state.newdiscoverPlanet[Math.floor(Math.random()*this.state.newdiscoverPlanet.length)];
this.setState({
currentPlanet: planetItem,
});
}
How do I get a different result from the same state?
<Text>{this.state.currentPlanet}</Text>
<Text>{this.state.currentPlanet}</Text>
<Text>{this.state.currentPlanet}</Text>
I know I could just add two more different states with all the items of newdiscoverPlanet but 1) I have a chance of getting the same results 2) It seems too lengthy for something that might have an easier solution.
Don't put the randomly generated name in the state, but instead, call the function to generate a random name multiple times in your render function.
Basically something like that should do the trick:
_getRandomPlanet(){
var planetItem = this.state.newdiscoverPlanet[Math.floor(Math.random()*this.state.newdiscoverPlanet.length)];
return planetItem
}
And in your JSX:
<Text>{this._getRandomPlanet()}</Text>
<Text>{this._getRandomPlanet()}</Text>
<Text>{this._getRandomPlanet()}</Text>
First of all if newdiscoverPlanet is constant it shouldn't be in the state (a file constant, or static member, an instance or even a prop member would do but it's not really a state of the component).
Then from what I understand of your questions it seems that you want a random selection of newDiscoverPlanet instead of just one.
And from what I read from a comment it also seems that you need to import image files for each planet.
So what about:
import sunpImg from '../img/sunp.png';
import twopImg from '../img/twop.png';
import bluepImg from '../img/bluep.png';
import purplepImg from '../img/purplep.png';
import bluepurplepImg from '../img/bluepurplep.png';
import redpImg from '../img/redp.png';
import orangepImg from '../img/orangep.png';
const planetsObj = [
{ name:'sunp', img: sunpImg },
{ name:'twop', img: twopImg },
{ name:'bluep', img: bluepImg },
{ name:'purplep', img: purplepImg },
{ name:'bluepurplep', img: bluepurplepImg },
{ name:'redp', img: redpImg },
{ name:'orangep', img: orangepImg },
];
class YouComponent extends Component {
state = {
randomPlanets: this.getRandomPlanets()
}
getRandomPlanets() {
// Note: since the randomization relies on random sorting
// you won't have the same planet twice, if you want a
// subset (less planets) just use .slice(numOfItem)
return [...planetsObj].sort(() => parseInt(Math.random() * 3, 10) - 1);
}
updateRandomPlanets() {
this.setState(() => ({ randomPlanets: this.getRandomPlanets() }));
}
render() {
const { randomPlanets } = this.state;
// Note: if you randomize during render the renders
// won't be consistent and the display won't be controllable
return (
{randomPlanets.map(planet => (
<div>
<img src={planet.img} />
<Text>{planet.name}</Text>
</div>
))}
);
}
}

Inject dynamic content react

I want to display content from my database into a react component.
The content can be a string or html, I know React don't really like html injections ...
I write a module but it's not working as I want
import $ from "jquery";
import React from 'react'
export default React.createClass({
getInitialState() {
return { content: [] };
},
componentDidMount: function() {
if (this.state.content.length == 0) {
$.get('/content', function(data) {
this.setState({
content: data
})
}.bind(this))
}
},
getValue() {
for (let i = 0; i < this.state.content.length; i++) {
if (this.state.content[i].key == this.props.contentKey) {
return this.data[i].value;
}
}
return this.props.contentKey;
},
render() {
return (<span>{this.getValue()}</span>)
}
})
this one will convert <Content contentKey="key"/> into <span>value</span>
I'd like for example
render() {
return (
<div>
{content('key1')}
<img src={content('key2')} />
</div>
)
}
let's say my server returns
{"key1": "<p>I am key 1 value</p>", "key2": "key2Value.jpg"}
The result should be something like
<div>
<p>I am key 1 value</p>
<img src="key2Value.jpg" />
</div>
And I don't want the html to be escaped
What would work is to create a class method such as below:
renderEls() {
return this.state.content.map(function(el) {
return (<div>
<p>{el.key1}</p>
<img src={el.key2} />
</div>)
});
}
Then, in your component's render method, you'd want to do something like this:
render() {
return <div>{this.renderEls()}</div>
}
Also, if you're going to be using React, it's generally best to split up everything into their own separate components.
In your case, it might not be a bad idea to create a new component that you'll render for each object returned from the server, passing in the values as props.
To render HTML from a string, React gives you a way to do so as outlined here: https://facebook.github.io/react/tips/dangerously-set-inner-html.html
Hope that helps!
You could use XHR instead of jquery.
var xhr = new XMLHttpRequest()
xhr.open("GET", './a.json', true)
with a callback in
xhr.onloadend()
but you would need
dangerouslySetInnerHTML={{ __html: content}}

owner-based and parent-based contexts differ (values: `` vs ``) for key

I am trying to use contextin Reactjs but I keep getting this warning from react i have tried playing with it as much as i can but nothing seems to work,
I read the post they send you too with all the comments, Didn't help much.
Followed a few online tutorials but cant seem to solve my problem, I am not sure what i am doing wrong.
the error: Warning: owner-based and parent-based contexts differ (values: `` vs ``) for key (filterItems) while mounting CategorieChooser
here is the layout basically
var Application = React.createClass({
childContextTypes: {
filterItems: React.PropTypes.array
},
getChildContext: function() {
return { filterItems: [] };
},
render: function() {
return (
<div>
<Banner />
<CategorieChooser />
</div>
);
}
});
var CategorieChooser = React.createClass({
contextTypes: {
filterItems: React.PropTypes.array
},
...
})

Passing AJAX Results As Props to Child Component

I'm trying to create a blog in React. In my main ReactBlog Component, I'm doing an AJAX call to a node server to return an array of posts. I want to pass this post data to different components as props.
In particular, I have a component called PostViewer that will show post information. I want it to by default show the post passed in from its parent via props, and otherwise show data that is set via a state call.
Currently, the relevant parts of my code looks like this.
var ReactBlog = React.createClass({
getInitialState: function() {
return {
posts: []
};
},
componentDidMount: function() {
$.get(this.props.url, function(data) {
if (this.isMounted()) {
this.setState({
posts: data
});
}
}.bind(this));
},
render: function() {
var latestPost = this.state.posts[0];
return (
<div className="layout">
<div className="layout layout-sidebar">
<PostList posts={this.state.posts}/>
</div>
<div className="layout layout-content">
<PostViewer post={latestPost}/>
</div>
</div>
)
}
});
and the child component:
var PostViewer = React.createClass({
getInitialState: function() {
return {
post: this.props.post
}
},
render: function() {
/* handle check for initial load which doesn't include prop data yet */
if (this.state.post) {
return (
<div>
{this.state.post.title}
</div>
)
}
return (
<div/>
)
}
});
The above works if I swap out the if statement and content in my child's render to this.props.* However, this would mean that I couldn't change the content later via state, correct?
TLDR: I want to set a default post to be viewed via props in a child component (results of an AJAX call), and I want to be able to change what post is being viewed by adding onClick events (of another component) that will update the state.
Is this the correct way to go about it?
Current hierarchy of my app's components are:
React Blog
- Post List
- Post Snippet (click will callback on React Blog and update Post Viewer)
- Post Viewer (default post passed in via props)
Thanks!
EDIT:
So what I ended up doing was attaching the props in ReactBlog using a value based on this.state. This ensured that it updates when I change state and renders correctly in child components. However, to do this I had to chain onClick callbacks up through all the various child components. Is this correct? It seems like it could get VERY messy. Here's my full example code:
var ReactBlog = React.createClass({
getInitialState: function() {
return {
posts: [],
};
},
componentDidMount: function() {
$.get(this.props.url, function(data) {
if (this.isMounted()) {
this.setState({
posts: data,
post: data[0]
});
}
}.bind(this));
},
focusPost: function(slug) {
$.get('/api/posts/' + slug, function(data) {
this.setState({
post: data
})
}.bind(this));
},
render: function() {
return (
<div className="layout">
<div className="layout layout-sidebar">
<PostList handleTitleClick={this.focusPost} posts={this.state.posts}/>
</div>
<div className="layout layout-content">
<PostViewer post={this.state.post}/>
</div>
</div>
)
}
});
var PostList = React.createClass({
handleTitleClick: function(slug) {
this.props.handleTitleClick(slug);
},
render: function() {
var posts = this.props.posts;
var postSnippets = posts.map(function(post, i) {
return <PostSnippet data={post} key={i} handleTitleClick={this.handleTitleClick}/>;
}, this);
return (
<div className="posts-list">
<ul>
{postSnippets}
</ul>
</div>
)
}
});
var PostSnippet = React.createClass({
handleTitleClick: function(slug) {
this.props.handleTitleClick(slug);
},
render: function() {
var post = this.props.data;
return (
<li>
<h1 onClick={this.handleTitleClick.bind(this, post.slug)}>{post.title}</h1>
</li>
)
}
});
var PostViewer = React.createClass({
getInitialState: function() {
return {
post: this.props.post
}
},
render: function() {
/* handle check for initial load which doesn't include prop data yet */
if (this.props.post) {
return (
<div>
{this.props.post.title}
</div>
)
}
return (
<div/>
)
}
});
Still hoping to get some feedback / hope this helps!
This is an old question, but I believe still relevant, so I'm going to throw in my 2 cents.
Ideally, you want to separate out any ajax calls into an actions file instead of doing it right inside a component. Without going into using something like Redux to help you manage your state (which, at this point in time, I would recommend redux + react-redux), you could use something called "container components" to do all of the heavy state lifting for you and then use props in the component that's doing the main layout. Here's an example:
// childComponent.js
import React from 'react';
import axios from 'axios'; // ajax stuff similar to jquery but with promises
const ChildComponent = React.createClass({
render: function() {
<ul className="posts">
{this.props.posts.map(function(post){
return (
<li>
<h3>{post.title}</h3>
<p>{post.content}</p>
</li>
)
})}
</ul>
}
})
const ChildComponentContainer = React.createClass({
getInitialState: function() {
return {
posts: []
}
},
componentWillMount: function() {
axios.get(this.props.url, function(resp) {
this.setState({
posts: resp.data
});
}.bind(this));
},
render: function() {
return (
<ChildComponent posts={this.state.posts} />
)
}
})
export default ChildComponentContainer;
A blog is static for the most part, so you could exploit React immutable structures to "render everything" all the time instead of using the state.
One option for this is to use a router (like page.js) to fetch data.
Here is some code http://jsbin.com/qesimopugo/1/edit?html,js,output
If you don't understand something just let me know ;)
Get rid of isMounted and make use of context if you're passing callbacks down several levels

How do I keep document.title updated in React app?

Since React doesn't have any builtin way to manage document.title, I used to set it inside componentDidMount of my route handlers.
However now I need to amend the title based on state fetched asynchronously. I started putting assingments into componentDidUpdate, but every now and then I forget to put document.title assignment into some pages, and previous title sticks around until I finally notice it.
Ideally I'd like a way to express document.title declaratively, without having to assign it. Some kind of “fake” component would probably be most convenient, given that I want to be able to specify the document title at several nesting levels:
On top level (the default title);
On page level (for some of the pages, but not all);
Sometimes, on inner component level (e.g. user typing into a field).
Additional requirements:
Title specified in child should override title specified by parent;
Reliable (guarantees cleanup on route change);
Should not emit any DOM (i.e. no hacks with component returning <noscript>);
I'm using react-router but it's better if this component works with other routers too.
Anything I can use?
I wrote react-document-title just for that.
It provides a declarative way to specify document.title in a single-page app.
If you want to get title on server after rendering components to string, call DocumentTitle.rewind().
Features
Does not emit DOM, not even a <noscript>;
Like a normal React compoment, can use its parent's props and state;
Can be defined in many places throughout the application;
Supports arbitrary levels of nesting, so you can define app-wide and page-specific titles;
Works on client and server.
Example
Assuming you use something like react-router:
var App = React.createClass({
render: function () {
// Use "My Web App" if no child overrides this
return (
<DocumentTitle title='My Web App'>
<this.props.activeRouteHandler />
</DocumentTitle>
);
}
});
var HomePage = React.createClass({
render: function () {
// Use "Home" while this component is mounted
return (
<DocumentTitle title='Home'>
<h1>Home, sweet home.</h1>
</DocumentTitle>
);
}
});
var NewArticlePage = React.createClass({
mixins: [LinkStateMixin],
render: function () {
// Update using value from state while this component is mounted
return (
<DocumentTitle title={this.state.title || 'Untitled'}>
<div>
<h1>New Article</h1>
<input valueLink={this.linkState('title')} />
</div>
</DocumentTitle>
);
}
});
Source
I keep track of mounted instances and only use title given to the top DocumentTitle in the mounted instance stack whenever it updates, gets mounted or unmounted. On server, componentWillMount fires but we won't get didMount or willUnmount, so we introduce DocumentTitle.rewind() that returns a string and destroys state to prepare for next request.
var DocumentTitle = React.createClass({
propTypes: {
title: PropTypes.string
},
statics: {
mountedInstances: [],
rewind: function () {
var activeInstance = DocumentTitle.getActiveInstance();
DocumentTitle.mountedInstances.splice(0);
if (activeInstance) {
return activeInstance.props.title;
}
},
getActiveInstance: function () {
var length = DocumentTitle.mountedInstances.length;
if (length > 0) {
return DocumentTitle.mountedInstances[length - 1];
}
},
updateDocumentTitle: function () {
if (typeof document === 'undefined') {
return;
}
var activeInstance = DocumentTitle.getActiveInstance();
if (activeInstance) {
document.title = activeInstance.props.title;
}
}
},
getDefaultProps: function () {
return {
title: ''
};
},
isActive: function () {
return this === DocumentTitle.getActiveInstance();
},
componentWillMount: function () {
DocumentTitle.mountedInstances.push(this);
DocumentTitle.updateDocumentTitle();
},
componentDidUpdate: function (prevProps) {
if (this.isActive() && prevProps.title !== this.props.title) {
DocumentTitle.updateDocumentTitle();
}
},
componentWillUnmount: function () {
var index = DocumentTitle.mountedInstances.indexOf(this);
DocumentTitle.mountedInstances.splice(index, 1);
DocumentTitle.updateDocumentTitle();
},
render: function () {
if (this.props.children) {
return Children.only(this.props.children);
} else {
return null;
}
}
});
module.exports = DocumentTitle;
Take a look at the NFL's react-helmet.
class Layout extends React.Component {
constructor(props){
super(props);
document.title = this.props.title;
}
render(){
return(
<div>
</div>
);
}
}
and then <Layout title="My Title"/> that easy!
Try react-frozenhead, it's actually more sophisticated than react-document-title - it allows us change title, description and anything else in section.
Meanwhile, 3 years have gone! ;-)
If you want to manipulate other page headers than title (like description, canonical, etc.), react-document-meta NPM dependency could be a good thing to use.

Categories

Resources