Gatsby build getting window error while using onLoad - reactjs

In my code im trying to get the div height by clientHeight by using onLoad. ComponentDdiMount doesnt give the clientHeight in this case as images load in the div.The code works fine for me.But when i try gatsby build i get the window error as im using onLoad. Is there any workaround for this?
export default class Mainpageanimation extends React.Component {
constructor(props) {
super(props);
this.topref = React.createRef();
}
load = () =>{
const centerheight = this.topref.current.clientHeight;
//animate div
}
render(){
return (
<div className="topdiv" ><img src={require("image.png") } ref={this.topref} onLoad=
{this.load}/></div>
);
}
}

Try including onLoad property only when the code is running in the browser
export default class Mainpageanimation extends React.Component {
constructor(props) {
super(props);
this.topref = React.createRef();
}
load = () =>{
const centerheight = this.topref.current.clientHeight;
//animate div
}
render(){
return (
<div
className="topdiv" >
<img src={require("image.png") } ref={this.topref}
{...typeof window !== 'undefined' ? {onLoad: this.load} : {}}
/>
</div>
);
}
}

With gatsby build, rather than gatsby develop, some objects such as window or document are not defined at the point your code is requesting them. The usual errors are fixed by checking if the window is defined in a componentDidMount lifecycle/useEffect hook before triggering your function. From Gatsby's documentation:
Some of your code references “browser globals” like window or
document. If this is your problem you should see an error above like
“window is not defined”. To fix this, find the offending code and
either a) check before calling the code if window is defined so the
code doesn’t run while Gatsby is building (see code sample below) or
b) if the code is in the render function of a React.js component, move
that code into a componentDidMount lifecycle or into a useEffect hook,
which ensures the code doesn’t run unless it’s in the browser.
So, my first approach will be checking if the window is defined:
load = () =>{
let centerHeight;
if (typeof window !== undefined){
centerHeight= this.topref.current.clientHeight;
//animate div
}
}
If this doesn't work, I would try to change your onLoad function and use it in a componentDidMount directly:
export default class Mainpageanimation extends React.Component {
constructor(props) {
super(props);
this.topref = React.createRef();
}
componentDidMount(){
let centerHeight;
if (typeof window !== undefined){
centerHeight= this.topref.current.clientHeight;
//animate div
}
}
render(){
return (
<div className="topdiv" ><img src={require("image.png") } ref={this.topref} /></div>
);
}
}
Besides the answer, I think you should bind your reference. In a functional component arrow functions does it automatically but in a stateful component like yours, you must do it manually. In your constructor:
this.topref= this.topref.bind(this);
You can check for further information in Refs and the DOM by React documentation.

Related

this.setState is not updating the state property

I am setting the state in a method call from a constructor but the state property is still null. Some modification on the code gives be this error in the console,
index.js:1375 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the ResultContainer component.
In my component constructor i am calling a method. That method in turn calls another method and is trying populate an state property with an array. I get two different variants of error/warning. In my code if uncomment the lines inside render method i get searchedAddress is null error. if i leave the lines as commented then in the console i get the above error.
On my render() method i can check for null and that certainly does not throw and error but the the result items are not getting loaded no matter what i do. this question, State not changing after calling this.setState seems somewhat relevant but i am not sure i could i re-render the item asynchronously.
import React from 'react';
import ResultItem from './ResultItem'
import AddressService from '../Api/AddressService'
class ResultContainer extends React.Component{
constructor(props){
super(props);
this.state = {
searchedAddresses : null
}
this.intiateSearch();
}
intiateSearch =() => {
if(this.props.searchedTerm != null){
var addressService = new AddressService();
//this is just getting an items from json for now. i checked and result does contain items
var result = addressService.searchAddress(this.props.searchedAddresses);
//the below line does not seem to work
this.setState({
searchedAddresses : result
});
}
}
render(){
return(
<div className="mt-3">
<h4>Seaching for postcode - <em className="text-primary">{this.props.searchedTerm}</em></h4>
<div className="container mt-1">
{
//below gives an error stating searchedAddresses is null
// this.state.searchedAddresses.map((address, index)=>{
// return(<ResultItem address={address}/>);
// })
}
</div>
</div>
);
}
}
export default ResultContainer;
You shouldn't call component functions inside of the constructor method, the component is not yet mounted at this point and therefore your component functions aren't available to be used here yet. In order to update your state. You used to be able to use the componentWillMount lifecycle method but that is now considered legacy. You should be calling any component initializing functions inside of the componentDidMount lifecycle method.
Change your code like so :
(Notice the check for state initially being null in the render function)
import React from 'react';
import ResultItem from './ResultItem'
import AddressService from '../Api/AddressService'
class ResultContainer extends React.Component{
constructor(props){
super(props);
this.state = {
searchedAddresses : null
}
}
componentDidMount() {
this.intiateSearch();
}
intiateSearch =() => {
if(this.props.searchedTerm != null){
var addressService = new AddressService();
//this is just getting an items from json for now. i checked and result does contain items
var result = addressService.searchAddress(this.props.searchedAddresses);
this.setState({
searchedAddresses : result
});
}
}
render(){
return(
<div className="mt-3">
<h4>Seaching for postcode - <em className="text-primary">{this.props.searchedTerm}</em></h4>
<div className="container mt-1">
{
this.state.searchedAddresses !== null &&
this.state.searchedAddresses.map((address, index)=>{
return(<ResultItem address={address}/>);
})
}
</div>
</div>
);
}
}
export default ResultContainer;
I see you're calling this.intiateSearch(); in constructor which will call setState on a not yet mounted component.
So why don't you call this.intiateSearch(); inside componentDidMount() lifecyle after the component is mounted?
componentDidMount() {
this.intiateSearch();
}

React props is passed, but only render() reads props?

I couldn't find a related situation to mines, however my problem I am having a common error of TypeError: Cannot read property 'props' of undefined.
Weird part is, this error is occurring only for the method I defined above render().
Inside of render() I am able to have access without errors though. React dev tools shows I even have access to props.
Code below:
import { Route } from 'react-router-dom'
import AuthService from '../../utils/authentication/AuthService'
import withAuth from '../../utils/authentication/withAuth'
const Auth = new AuthService()
class HomePage extends Component {
handleLogout() {
Auth.logout()
this.props.history.replace('/login')
}
render() {
console.log(this.props.history)
return (
<div>
<div className="App-header">
<h2>Welcome {this.props.user.userId}</h2>
</div>
<p className="App-intro">
<button type="button" className="form-submit" onClick={this.handleLogout}>Logout</button>
</p>
</div>
)
}
}
export default withAuth(HomePage)
Edit: Apologies. I don't want to cause a confusion either, so I will add that I am also using #babel/plugin-proposal-class-propertiesto avoid this binding.
It's because your method handleLogout has it's own context. In order to pass the this value of the class to your method have to do one of two things:
1) Bind it inside the constructor of the class:
constructor(props) {
super(props)
this.handleLogout = this.handleLogout.bind(this)
}
2) You declare your handleLogout method as an arrow function
handleLogout = () => {
console.log(this.props)
}
this isn't bound in non es6 I believe. So you could either bind it with a constructor, or you may be able to get away with an es6 type function
handleLogout = () => {
Auth.logout()
this.props.history.replace('/login')
}
I can't try this, but you could also do a
constructor(props) {
super(props);
// Don't call this.setState() here!
this.handleLogOut= this.handleLogOut.bind(this);
}
You need to use .bind on your click handler.
<button type="button" className="form-submit" onClick={this.handleLogout.bind(this)}>Logout</button>

Debugging es6 in create-react-app

I am trying to debug create-react-app and when I put a breakpoint on an arrow function I have invalid value inside of this keyword and completely strange behavior of stopping breakpoints (the devtools don't allow to put a breakpoint at a valid js expression, it looks like disabled for a breakpoint. I checked on both FF and Chrome browsers. However, when I change arrow function (()=>{}) to the function(){} declaration, the debugging behavior is correct. Does anyone know what the issue is and what react start up project would you recommend where arrow functions are debugged correctly?
My code in App.js looks like here. Try to put a breakpoint inside of the line:
this.setState({value: this.state.value + 1})
this should be App but it's not the case. It is undefined at this particular breakpoint, although the app's behavior is correct. I suspect something is broken with sourcemaps... What are react projects out there with good setup with webpack that handles sourcemaps correctly?
Without using something like let that = this you can use functions for callbacks in JSX properties in a couple of different ways.
If you want to use it directly as an anonymous function you can use like that:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
}
render() {
return (
<button onClick={(function () {
this.setState(prevState => ({ value: prevState.value + 1 }));
}).bind(this)}>
Click me
</button>
);
}
}
You are binding the function to this here directly. I haven't seen any example of that. Generally people use something like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({ value: prevState.value + 1 }));
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Here we are using our callback function as a reference and binding it in out constructor.
Another alternative is using an arrow function. For this situation you don't need to bind your function. Also, we are using class-fields here, hence no need to use constructor at all.
class App extends React.Component {
state = { value: 0 };
handleClick = () => {
this.setState(prevState => ({ value: prevState.value + 1 }));
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
In a callback for JSX props this' scope changes, it does not refer to the class anymore. So either you will bind it to this or use an arrow function which does not change the scope of this.
Sometimes debug tools can struggle to correctly place breakpoints for lambda functions in these cases. Perhaps you could achieve the same effect by temporarily adding debugger to your source code as follows, to force the breakpoint to hit:
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
}
render() {
return (
<div className="App">
<header className="App-header" onClick={() => {
debugger; // ADD THIS: It will act as a breakpoint and force the developer-tools to break so you can step through the code
this.setState({value: this.state.value + 1})
}}>
<img src={logo} className="App-logo" alt="logo"/>
<h1 className="App-title">Welcome to React, the state is {this.state.value}</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>);
}
}
export default App;

get height of image on load and send to parent

I am trying to get the height of an image when it has loaded and send it back to the parent component, but it is causing infinite rerendering.
This is a prototype of my code:
import MyImage from './images/myImage.jpg';
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
height: 0
}
}
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
this.setState({
height: height
});
this.props.setUnitHeight(height);
}
render() {
const image = this.props.image;
return (
<img src={image.name} onLoad={(e)=>{this.getHeight(e)}} />;
);
}
}
class App extends Component {
constructor(props) {
super(props);
const initUnit = 78.4;
this.state = {
unit: initUnit
}
}
setUnitHeight = (height) => {
this.setState({
unit: height
});
}
render() {
return (
<div>
<Image image={MyImage} setUnitHeight={this.setUnitHeight} />
</div>
);
}
}
I have tried sending unit as a prop and then checking in shouldComponentUpdate whether it should be rerender or not, but that did nothing.
The issue you are having is that React by default re-renders the component every time you call this.setState. In your case what this is happening:
You load your Image component
It loads the <img> tag and fires the onLoad function
The onLoad function calls this.setState
Repeat these steps forever
You should take a look at the React's lifecycle components methods (https://reactjs.org/docs/react-component.html#the-component-lifecycle) to understand this better.
My suggestion is: do not keep the image height in the state, unless you really need it. If you really need to maintain it in the state for some reason you can use the lifecycle method shouldComponentUpdate (https://reactjs.org/docs/react-component.html#shouldcomponentupdate`) to prevent it from rendering.
Your code seems redundant, setState({}) isn't necessary in <Image> class. If you are using the same props throughout the app, then you should be setting it at one place and be using the same prop all over. For example -
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
//setState not needed here
this.props.setUnitHeight(height);
}
That should do it.
P.S: Do check if your this references aren't going out of scope.

Where this points in setState in ReactJs

I am stuck at understanding the this keyword in the following code of React. I hope you guys can clear me. Please check my simple code of React JS.
Board Class render
return( <Comment key={i} index={i} >
{value}
</Comment>
in comment class
constructor(){
super();
this.state = {editing:false}
}
render(){
return(<div className="commentContainer">
<p>{this.props.children}</p>
<button onClick={this.edit} className="button-primary">Edit</button>
<button onClick={this.remove} className="button-danger">Remove</button>
</div>)
}
edit(){
alert('comment')
this.setState({editing:true});
}
I am calling Comment from Board component so in Comment render function this will refer to Board or Comment class?
If it is pointing to Board then on click of edit button it should show error that edit() function is not defined in Board class?
If it is pointing to Comment class (which is actually because when I clicked it gives alert) then under edit function I am using this.setState so it should update editing state which is not happening and giving error of "undefined setState"
and when I use
<button onClick={this.edit.bind(this)} className="button-primary">Edit</button>
it works which means it was not pointing to Comment that's why I need to bind it with Comment. Total confusion, I saw a video where simply this.setState working,
Also If its pointing to comment then how it gets value of this.props.children
Can anyone clear me please.
The auto-binding of this in the render() function is not possible using React class syntax. If you use React.createClass({}) instead of class extends React it will be automatic. If you prefers use classes, you can bind methods with this manually in the class constructor :
constructor() {
super();
this.edit = this.edit.bind(this);
}
You also can use new possibilities of es2016 syntax (with some transpiler configuration) and use fat arrow function as class instance property :
class MyComponent extends React.Component {
state = { editing: false };
edit = () => { /* method content */ }
render() {
// ...
}
}
See this article for more informations.

Resources