React update state if image not found. - reactjs

I can't seem to wrap my head around this problem. What I'm trying to do is have React attempt to verify an image is not returning a 404 to display an alternative image instead. Something like the below does not work as React does not wait for the image to attempt to load for returning the component.
getInitialState: function () {
var img = new Image();
img.onerror = function() {
img.src = "alternativeImage.jpg"
},
img.src = this.props.image;
return {image: <img src={img.src} />};
},
The above code will display the images just fine but the 404 alternative image does not show up.
I've tried to place this in another method outside of getInitialState. I've also tried to have it call an external method outside of the React component, but nothing works.
There is a short hand method of adding onerror attribute on the image tag itself, but it seems React has the same issue of not executing that code and or updating itself based on the outcome.
I think the most frustrating part is that I cannot seem to have any function called that React would be able to work with from the JavaScript image object.
Thanks for any help you can provide.

Here's a solution that I came up with. Pretty new to React and programming in general so take this with a grain of salt....
const DEFAULT_IMAGE="http://i.imgur.com/lL3LtFD.jpg"
<img src={this.props.SRC} onError={(e)=>{e.target.src=DEFAULT_IMG}}/>
WARNING: If the default constant is invalid, the browser will be caught in an infinite loop (at least when I tested this in Chrome).
I haven't found a solution to this yet, but in vanilla javascript, you set the onerror attribute to null which tells the browser to make only a single request for the asset.
jQuery/JavaScript to replace broken images

It's a bad habit to put components into state. What you probably want is something like the following:
var Img = React.createClass({
componentDidMount: function () {
var self = this;
var img = new Image();
img.onerror = function () {
self.setState({ src: 'http://i.imgur.com/lL3LtFD.jpg' });
};
img.src = this.state.src;
},
getInitialState: function () {
return { src: '/404.jpg' };
},
render: function () {
return <img src={this.state.src} />;
}
});
jsfiddle link: http://jsfiddle.net/e2e00wgu

Related

'Dashboard not a constructor' error with Google Charts

I am a naive React Developer and facing some difficulty with getting gooogle chart work with react. I am using Google Charts in a ReactJs component with ControlWrapper as shown below.
componentDidMount: function(){
google.charts.load('current', {'packages':['corechart', 'controls']});
this.drawCharts();
},
componentDidUpdate: function(){
google.charts.load('current', {'packages':['corechart', 'controls']});
this.drawCharts();
},
drawCharts: function(){
var cmpt = this;
//Removed detailed code from here due to copyright issues
//adding controls----------------
let dashboard = new google.visualization.Dashboard( document.getElementById(cmpt.props.widgetId) );
let controlId = '${this.props.widgetId}_control';
var controlWrapper = new google.visualization.ControlWrapper({
'controlType' : 'NumberRangeFilter',
'containerId' : controlId,
'options' : {
'filterColumnLabel' : xDataSysName
}
});
var barChart = new google.visualization.ChartWrapper({
'chartType': 'BarChart',
'containerId': this.props.widgetId,
'options': options
});
dashboard.bind(controlWrapper, barChart);
dashboard.draw(data);
if(linkColumn){
let selectionEventHandler = function() {
window.location = data.getValue(barChart.getSelection()[0]['row'], 1 );
};
google.visualization.events.addListener(barChart, 'select', selectionEventHandler);
}
}
},
This is not the whole piece of code but should be enough for the issue I'm facing.
First time I load the page, I get the error in the console saying
google.visualization.Dashboard is not a constructor
I reload the page hitting SHIFT+F5, the error goes away and components load just fine except ones that are dependent on controlWrapper throwing the error as follows
google.visualization.controlWrapper is not a constructor
which never goes away even after reloading the page. I referred to this discussion and tried their solution but I am still getting these error in the manner mentioned above. Please help me figure out how to fix it. Also, I am not able to comprehend why dashboard error goes away on a reload.
need to wait until google charts has fully loaded before trying to use any constructors,
this is done by providing a callback function.
try changing the load statement as follows...
componentDidMount: function(){
google.charts.load('current', {packages:['corechart', 'controls'], callback: this.drawCharts});
},
componentDidUpdate: function(){
google.charts.load('current', {packages:['corechart', 'controls'], callback: this.drawCharts});
},

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));

How to use NPM installed react component in JSX?

I'm trying to use a 3rd party react component (react-slick) within my JSX and running into a problem. I'm attempting to use it very similarly to within this example by the app creator (example1.jsx snippet below).
Whenever I do this I get 2 warnings and an error:
Warning: This JSX uses a plain function. Only React components are valid in React's JSX transform.
Warning: Something is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory
Uncaught TypeError: Cannot read property '__reactAutoBindMap' of null
I have also tried something similar as example2.jsx, which was a solution found in another Stack Overflow question. However, while no warnings, or errors were thrown, in this case the code did not render at all.
I have tried various methods of using React.createFactory (which shouldn't be used in JSX to begin with), and other finagling but with no results.
This is probably a stupid question but how the hell do I use NPM installed components in my JSX?
example1.jsx:
var React = require('react');
var Slider = require('react-slick');
var SingleItem = React.createClass({
render: function () {
var settings = {
dots: true,
}
return (
<div>
<h3> Image slider with one item at a time</h3>
<Slider dots={true}>
<div><img src="/img/autumn.jpg" alt=""/></div>
<div><img src="/img/sun.jpg" alt=""/></div>
</Slider>
</div>
);
}
});
var App = React.createClass({
render: function () {
return (
<div className='container'>
<SingleItem />
</div>
);
}
});
example2.jsx:
var App = React.createClass({
render: function () {
return (
<div className='container'>
{SingleItem}
</div>
);
}
});
It looks like you aren't actually referencing the slider component. You may find that your solution looks more like
var Slick = require('react-slick');
var Slider = Slick.Slider
Use the debugger to check what object you are actually being given by the require. It is also possible that you need to require a component by directly referencing one of the libraries file e.g.
var Slider = require('./slider.jsx');
Take a look at the lib or dist directory also, you can see the module.exports value for your require.
Use this to include Slick Slider component:
var Slider = React.createFactory(require('react-slick'));

React parent renders with initial state before successful ajax call sets state

I'm using React with webpack and babel for compiling jsx. I have a parent component that looks like this:
const Menu = module.exports = React.createClass({
loadUser() {
let xhr = new XMLHttpRequest();
xhr.open('GET', this.state.url, true);
xhr.onload = function() {
let data = JSON.parse(xhr.responseText);
this.setState({
user: data
});
}.bind(this);
xhr.send();
},
componentDidMount() {
this.loadUser();
},
getInitialState() {
return {
url: this.props.url,
user: []
};
},
render: function() {
return (
<div className="menu">
<Nav user={this.state.user} />
...
</div>
);
}
});
As you can see, I attempt to use this.setState(...) to set this.state.user to the data received from the XMLHttpRequest. However, when I try to access it in the child, Nav, by simply calling, console.log(this.props.user), only an empty array, i.e. [], is printed.
What am I doing wrong here? I've read the React docs and I'm stumped. In the following tutorial, data is fetched from the server and transferred to the child component in a manner similar to what I've done (above). Any help would be greatly appreciated. If I need to supply any additional code or context, let me know. Thanks.
getInitialState is used at the first renderso it's normal it's complete before your ajax call since the ajax call is performed in ComponentDidMount which is triggered just after the first rendering.
Before the ajax call is empty your state.user will be empty, then when the data are received it should update your view with the new data.
In my opinion you're not doing anything wrong it depends on what you want to do.
For example you could put a message in getinitialstate like mgs:"Please wait data are fetching" and remove this msg when your data arrive.
Otherwise if you absolutely need your data to be ready before rendering your component you can use that : https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html Read carefully it may not fit your use.
Talking for myself I would put a loading msg in getinitialstate and proceed the way you do.

Global React does not play nice with AMD React

I'm getting weird weird behaviour when rendering a component using an AMD-loaded React, when a global React already exists on the page. Click events on components are getting fired when they should not be.
A look at the DOM implies that this stems from multiple React instances (one global, one AMD in my case) not being aware of each other, but this poses a problem when loading an AMD module at runtime that depends on React, into a page that also includes React.
How can I resolve this clash?
Reproduction
I can make a component like this:
var ButtonComponent = React.createClass({
onButtonClick: function(){
alert(this.props.data + ' click event fired');
},
render: function() {
return React.DOM.button({onClick: this.onButtonClick}, this.props.data);
}
});
(function(){ // create vanilla
var ButtonList = React.createClass({
render: function() {
return React.DOM.div({}, React.createElement(ButtonComponent, {data: this.props.data}));
}
});
React.render(React.createElement(ButtonList, {data: 'button that was loaded by the page'}), document.getElementById('page-load-target'));
})();
jsbin
But as soon as I add another component using another instance of React then click the first button, then it calls the click event on the second loaded button:
// .... as above ....
(function(){ // create using amd
require.config({
paths: {
'react': '//fb.me/react-with-addons-0.12.2.min'
}
});
window.setTimeout(function(){
require(['react'], function(ReactFromAmd){
ReactFromAmd.render(ReactFromAmd.createElement(ButtonComponent, {data: 'button that was loaded by AMD'}), document.getElementById('amd-load-target'));
});
}, 1000)
})();
jsbin
If I use the existing, global version of React in this call (rather than ReactFromAmd, then it works as expected. jsbin
The ancestors (React instance) of ButtonComponent and the component created with ReactFromAmd.createElement are different, and yet they are in the same virtual DOM -- That's not allowed.
If you don't mind replacing AMD with browserify, I just figured out a way to let isolated/remotely-loaded React components co-exist nicely.
(To be continued if someone needs it)
This has been fixed in version 0.14.2: http://jsbin.com/tesodoxape/1/edit?html,js,output

Resources