react components not talking to each other - reactjs

I've built a basic restuarant recommendation app that filters by location using the YELP api. The api was responding to my requests with the response object and everything was appending to my divs perfectly, but I realized that for my project, I needed to make a new layer for the data listing. Here are the relevant portions of my two components as they are now:
display-recs:
var DisplayRecs = React.createClass({
render: function() {
var recsLoop = [];
if (this.props.recommendations) {
for (var i=0; i < this.props.recommendations.length; i++) {
recsLoop.push(<Recommendations item={this.props.recommendations[i]} />)
}
}
console.log(this.props.recommendations);
return (
<div className="DisplayRecs">
{recsLoop}
</div>
);
}
});
var mapStateToProps = function(state, props) {
return {
recommendations: state.recommendations
};
};
recommendations:
var Recommendations = React.createClass({
render: function() {
<div id="bizData">
<div id='nameList'>{this.props.item.name}</div>
<div id='phoneList'>{this.props.item.phone}</div>
<div id='ratingList'>{this.props.item.rating}</div>
</div>
}
});
var mapStateToProps = function(state, props) {
return {
recommendations: state.recommendations
};
};
I cannot figure out why the nameList, phoneList, and ratingList will not print onto the dom. When I view the elements tab in my devtools, all i see is an empty displayrecs div. I've tried to just change things by guessing, but it's not been fruitful. Can any of you see an obvious problem with the current code?
Thanks

Your Recommendations react component's render function doesn't have any return statement. Try doing this:
var Recommendations = React.createClass({
render: function() {
return ( <div id="bizData">
<div id='nameList'>{this.props.item.name}</div>
<div id='phoneList'>{this.props.item.phone}</div>
<div id='ratingList'>{this.props.item.rating}</div>
</div>);
}
});
Also add a key to the Recommendations components as #Vikramaditya recommends:
recsLoop.push(<Recommendations key={i} item={this.props.recommendations[i]} />)

Related

React Cannot read property '__reactInternalInstance$2f71vks24hx' of null [duplicate]

Here is the problematic component in question.
const UserList = React.createClass({
render: function(){
let theList;
if(this.props.data){
theList=this.props.data.map(function(user, pos){
return (
<div className="row user">
<div className="col-xs-1">{pos}</div>
<div className="col-xs-5">{user.username}</div>
<div className="col-xs-3">{user.recent}</div>
<div className="col-xs-3">{user.alltime}</div>
</div>
);
}, this);
} else {
theList = <div>I don't know anymore</div>;
}
console.log(theList);
return (
theList
);
}
});
Whenever I attempt to return {theList}, I receive a Cannot read property '__reactInternalInstance$mincana79xce0t6kk1s5g66r' of null error. However, if I replace {theList} with static html, console.log prints out the correct array of objects that i want. As per the answers, I have tried to return both {theList} and theList but that didn't help.
In both cases, console.log first prints out [] which I assume is because componentDidMount contains my ajax call to get json from the server and has not fired yet before the first render(). I have tried to check against
this.props.data being null but it does not help.
Here is the parent component if it helps:
const Leaderboard = React.createClass({
getInitialState: function(){
return ({data: [], mode: 0});
},
componentDidMount: function(){
$.ajax({
url: 'https://someurlthatreturnsjson',
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('https://someurlthatreturnsjson', status, err.toString());
}.bind(this)
});
},
render: function(){
return (
<div className="leaderboard">
<div className="row titleBar">
<img src="http://someimage.jpg"></img>Leaderboard
</div>
<HeaderBar />
<UserList data={this.state.data}/>
</div>
);
}
});
Ah OK, there were some interesting problems in here, but you were so close. The big one, with react you must always return a single top-level element (e.g. a div). So, your variable theList was actually an array of divs. You can't return that directly. But you can return it if it's wrapped in a single parent div.
const mockData = [
{
username: 'bob',
recent: 'seven',
alltime: 123,
},
{
username: 'sally mae',
recent: 'seven',
alltime: 133999,
},
];
var $ = {
ajax(opt) {
setTimeout(() => {
opt.success(mockData);
}, 200);
}
}
const UserList = React.createClass({
render: function(){
let theList;
if (this.props.data && this.props.data.length) {
theList = this.props.data.map(function(user, pos){
return (
<div key={user.username} className="row user">
<div className="col">{pos}</div>
<div className="col">{user.username}</div>
<div className="col">{user.recent}</div>
<div className="col">{user.alltime}</div>
</div>
);
});
} else {
theList = <div>There is NO data</div>;
}
return <div>{theList}</div>;
}
});
const Leaderboard = React.createClass({
getInitialState: function(){
return ({data: [], mode: 0});
},
componentDidMount: function(){
$.ajax({
url: 'https://someurlthatreturnsjson',
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error('https://someurlthatreturnsjson', status, err.toString());
}.bind(this)
});
},
render: function(){
return (
<div className="leaderboard">
<UserList data={this.state.data}/>
</div>
);
}
});
ReactDOM.render(
<Leaderboard/>,
document.getElementById('container')
);
.col {
width: 200px;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
To explain the fiddle a little bit. Don't worry about the weird looking var $ stuff, I'm just stubbing out jQuery's ajax method so I can return some fake data after 200ms.
Also, for me jsfiddle gives me a 'bad config' message when I run it, but I close the message and the result is there. Don't know what that's about.
return (
{theList}
)
should just be
return theList
because you are not inside JSX at that point. What you're doing there will be interpreted as
return {
theList: theList
}
That's ES6 shorthand properties syntax.
Error can also arise from accessing nested state that doesn't exist:
I lack the reputation to comment, so adding an answer for future assistance -- I ran into this same issue for a different reason. Apparently, the error is triggered from an earlier error throwing off react's internal state, but the error is getting caught somehow. github issue #8091
In my case, I was trying access a property of state that didn't exist after moving the property to redux store:
// original state
state: {
files: [],
user: {},
}
// ... within render function:
<h1> Welcome {this.state.user.username} </h1>
I subsequently moved user to redux store and deleted line from state
// modified state
state: {
files: [],
}
// ... within render function (forgot to modify):
<h1> Welcome {this.state.user.username} </h1>
And this threw the cryptic error. Everything was cleared up by modifying render to call on this.props.user.username.
There is a small problem with the if statement:
if(this.props.data !== []){
should be:
if(this.props.data){
this.props.data is null, if the ajax call returns null. alternatively the code could be more elaborate.
const data = this.props.data;
if(data && data.constructor === Array && data.length > 0) {
Not sure if this is how you want to do it, but it works for me.
edit:
const UserList = React.createClass({
render: function() {
if(this.props.data){
return this.props.data.map(function(user, pos){
return (
<li> className="row user">
<span>{pos}</span>
<span>{user.username}</span>
<span>{user.recent}</span>
<span>{user.alltime}</span>
</li>
);
});
} else {
return <li>I don't know anymore</li>;
}
}
});
I encountered this error when I rendered a stateless component and decided to remove the react-root-element (wrapping div) after rendering it with basic dom manipulation.
Conclusion: be aware of this, better don't do it.

Reflux store data rendering with line breaks and tag interpolation ES5

I do not want to use Babel/ES6 yet because reasons. I have been watching the egghead.io videos on react/reflux and have a component here I am rendering. It connects to the randomuser API and pulls 10 users into the store, when the store is updated it renders the user data onto the page. With the ES6 code shown in the video it allows nice interpolation of tags, so that but in my case I am just using lodash as _.map which operates slightly differently, and I am unable to find a way to render tag interpolation or even line breaks, as React renders out the elements as all children of one parent tag contained inside its own span tags.
The rendered code looks like this:
and my code is here:
var React = require('react');
var Reflux = require('reflux');
var request = require('superagent');
var _ = require('lodash');
var store = Reflux.createStore({
data: {users:[]},
init: function(){
request
.get("http://api.randomuser.me/?results=10")
.end(function(err,res){
if(err){
console.log(err)
}else {
var FirstName = res.body.results[0].user.name.first;
var LastName = res.body.results[0].user.name.last;
var picture = res.body.results[0].user.picture.thumbnail;
store.trigger({users:res.body.results})
}
});
},
getInitialState(){
return this.data;
}
});
var Name = React.createClass({
mixins:[Reflux.connect(store)],
render: function(){
return(
<div>
{_.map(this.state.users,function(n){
fName=n.user.name.first
lName=n.user.name.last
picture = n.user.picture.thumbnail;
return ""+fName+" "+lName + " " + picture
})
}
</div>
)
}
});
React.render(<Name />, document.getElementById('users'));
Any suggestions or advice would be greatly appreciated! also the egghead.io videos are top notch, i must give credit where it is due!
Personally, I try to avoid doing interpolation in JSX tags. JSX gives you a pretty solid API for constructing DOM elements! In this case, I'd do something like this:
render: function() {
var userElements = _.map(this.state.users,function(n){
var fName=n.user.name.first
var lName=n.user.name.last
var pictureURL = n.user.picture.thumbnail;
return (
<div className='user'>
<span className='first-name'>{fname}</span>
<span className='last-name'>{lname}</span>
<img className='picture' src={pictureURL}></img>
</div>
)
})
return (
<div className='user-container'>
{userElements}
</div>
)
}

ReactJS: How can I get the owner of a component?

ReactJS encourages one-way data flow but I want to break it for easier development where I need to two-way bound Input box.
I want a component like this
var App = React.createClass({
getInitialState: function(){
return {
user: {
name: ''
}
}
},
render: function(){
return <TwoWayBinder type="input" model="user.name" />;
}
});
where user.name is a variable in this.state. So, I want <TwoWayBinder /> component to access the state of it's parent component (which is an anti-pattern according to React philosophy). I see that parent component is available in _owner property of TwoWayBinder component.
Is that the only way to access the owner? I don't want to use valueLink for multiple reasons.
There is no documented api for accessing the owner. _owner is the only undocumented way (as far as I know).
Update: "component._owner is no longer available in 0.13" -zbyte
I'm not a fan of valueLink personally. I've been working on a similar but more powerful system.
In its lowest level form your code looks like this: jsbin 1
var App = React.createClass({
mixins: [formMixin],
getInitialState: function(){
return {
data: { name: "", email: "" }
}
},
render: function(){
var formData = this.stateKey("data");
return (
<div>
<input type="text" value={this.state.data.name} onChange={formData.forKey("name").handler()} />
<input type="text" value={this.state.data.email} onChange={formData.forKey("email").handler() } />
</div>
);
}
});
That's not bad, and gives you a lot of control, but you might want something even quicker. jsbin 2
var Input = React.createClass({
render: function(){
var link = this.props.link;
return <input type="text"
{...this.props}
value={link.getCurrentValue()}
onChange={link.handler()} />
}
});
var App = React.createClass({
mixins: [formMixin],
getInitialState: function(){
return { data: { name: "", email: "" } }
}
},
render: function(){
var formData = this.stateKey("data");
return (
<div>
<Input link={formData.forKey("name")} />
<Input link={formData.forKey("email")} />
</div>
);
}
});
For completeness, here's the full mixin:
var formMixinHandler=function(thisArg,keys,parent){return{forKey:function(key){return formMixinHandler(thisArg,keys.concat(key),this)},bindTo:function(newThisArg){return formMixinHandler(newThisArg,keys,this)},transform:function(fn){var obj=formMixinHandler(thisArg,keys,this);obj.transforms=obj.transforms.concat(fn);return obj},transforms:parent?parent.transforms:[],handler:function(){var self=this;return function(event){var value=event;if(event.target instanceof HTMLInputElement)if(event.target.type==="checkbox"||event.target.type==="radio")value=event.target.checked;else value=event.target.value;self.transforms.reduce(function(last,fn){return fn(last,event)},value);var targetObj=keys.slice(0,-1).reduce(function(obj,key){if(!obj[key])obj[key]={};return obj[key]},thisArg.state);targetObj[keys[keys.length-1]]=value;var updateObject={};updateObject[keys[0]]=thisArg.state[keys[0]];thisArg.setState(updateObject)}},getCurrentValue:function(){return keys.reduce(function(obj,key){return obj?obj[key]:null},thisArg.state)}}};var formMixin={stateKey:function(key){return formMixinHandler(this,[].concat(key))}};
Just for the question, there is really internal API to get the owner in version 0.13:
this._reactInternalInstance._currentElement._owner._instance
As you know, it's really not recommended.

Testing React Class is Of Type

I'm attempting to test a react class which renders out several other react classes. All working perfectly apart from i'm not totally sure about the best practise in terms of testing this.
Code:
Parent Class:
module.exports = function (deps) {
var MixLink = require('views/components/mixLink')(deps);
return React.createClass({
render: function() {
return (
<div class="packshotData mixesPage" data-view="mixlist">
{
this.props.mixes.map(function (mix) {
return <MixLink mix={mix} />;
})
}
</div>
);
}
});
};
Child Class:
module.exports = function (deps) {
var Link = require('views/components/link')(deps);
var mixLink = React.createClass({
render: function() {
return (
<div className="packshotData-item packshotData-item-overlay">
<Link className="packshotData-item-link" href={this.props.mix.href} data-control="mixplay">
<img className="packshotData-item-image packshotHeadphones" src={this.props.mix.thumbnailHref} />
<div className="packshotData-item-title-overlay">
<span className="packshotData-item-title-text">{this.props.mix.name}</span>
</div>
</Link>
</div>
);
}
});
return mixLink;
};
Test:
describe('mixLinkList Component', function () {
var MixLinkList = require('views/components/mixLinkList')({}),
MixLink = require('views/components/mixLink')({}),
TestUtils = React.addons.TestUtils;
it('is a function', function () {
expect(MixLinkList).to.be.a(Function);
});
it('is create a MixLink for every mix', function () {
var mixes = [
{
href: 'http://mixlink.com/One',
name: "thunderbirds",
thumbnailHref: 'someUri'
},
{
href: 'http://mixlink.com/Two',
name: "captain scarlet",
thumbnailHref: 'someOtherUri'
}
],
renderedMixLinkList,
renderedComponents;
renderedMixLinkList = TestUtils.renderIntoDocument(
<MixLinkList mixes={mixes} />
);
renderedComponents = TestUtils.findAllInRenderedTree(renderedMixLinkList, function (elem) {
return TestUtils.isCompositeComponentWithType(elem, MixLink);
});
expect(renderedComponents.length).to.be(2);
});
});
The test currently fails.
I can achieve my goals by grabbing the DOM node and checking the actual HTML which to me seems messy as the HTML produced by MixLink is the concern of MixLink Class not the MixLinkList class.
What i would like to do is simply interrogate the rendered component and check it has two children of type MixLink. Is that possible?
Thanks in advance!
For the most part your test seems fine. I think the reason it's failing is that MixLinkList is creating its own MixLink component distinct from the one in the test. You create one MixLink component on line 2 of the parent class, and a different one on line 3 of the test. So the rendered list does not contain elements of the test's MixLink, but rather elements of the list's own link component.
Your method of dependency injection is both the problem and the solution. Simply change the parent class to begin:
module.exports = function (deps) {
var MixLink = deps.MixLink;
And the test can then inject the MixLink component like this:
var MixLink = require('views/components/mixLink')({}),
MixLinkList = require('views/components/mixLinkList')({
MixLink: MixLink
}),
TestUtils = React.addons.TestUtils;
You might also consider using a more specific TestUtils methods such as scryRenderedComponentsWithType (docs), which makes the last part of your test read easier:
renderedComponents = TestUtils.scryRenderedComponentsWithType(
renderedMixLinkList,
MixLink
);

React CCSTransitionGroup animation not applying enter/leave classes

I'm trying to write a simple page slider. Here, when I click a page, it creates a new Page with random content, and re-renders the App component. On App render(), instead of the TransitionGroup holding both state.pages until animation completes, it just switches out the pages, never attaching the enter-leave classes and not performing the css animation. I'm sure I'm messing something up in the LifeCycle, but can't think of it.
Thanks for looking!
var Page = React.createClass({
handleClick: function(){
var pgs = ['page-one','page-two','page-three','page-four']
currentIdx = Math.floor(Math.random() * pgs.length);
var pg = pgs[ currentIdx ];
var newPg = <Page html={pg} title={'Title for ' + pg} />;
React.renderComponent(<App newPage={newPg} />, document.body)
},
render: function(){
return (<div className="content" style={{paddingTop: 44}} onClick={this.handleClick}>{this.props.html}</div>);
}
})
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var App = React.createClass({
getInitialState: function() {
return {pages: [<Page html="initial page" title="initial title" />]};
},
componentWillMount: function(){
this.setState({pages: [this.props.newPage]})
},
componentWillReceiveProps: function(nextProps) {
this.setState({pages: [nextProps.newPage]});
},
render: function() {
var title = this.state.pages.length ? this.state.pages[ this.state.pages.length - 1 ].props.title : 'none';
return (
<div id="body">
<TitleBar title={title} />
<ReactCSSTransitionGroup transitionName="pg" id="transdiv" component={React.DOM.div}>
{this.state.pages}
</ReactCSSTransitionGroup>
</div>
);
}
});
The problem was that I was setting the Page keys in Page.render() (not shown above), and not in App.render() I'm not sure why you can't set keys in the child/owned component as long as they're unique, but this fixed my problem.
var App = React.createClass({
// other methods are same
render: function(){
var title = 'Title';
var pgs = this.state.pages.map(function(pg){
// SET KEY HERE
pg.props.key = pg.props.title;
return pg;
}
return (
<div id="body">
<TitleBar title={title} />
<ReactCSSTransitionGroup transitionName="pg" id="transdiv" component={React.DOM.div}>
{pgs}
</ReactCSSTransitionGroup>
</div>
);
}
}
Also, if anyone can tell me the correct way to set props on unmounted components, please tell me. Setting them directly works, but it doesn't feel right.

Resources