In a new create-react-app, hot reloading seems to be taking effect only for css files. In the simple example below, clicking on the first hello increments the timer to 1. Then, when I change e.g. the second "Hello" text to "Goodbye", the page reloads and the timer is set to 0 again.
Am I misunderstanding hot-reloading perhaps? I thought that it's purpose is to stop the re-render from happening.
import React from 'react';
import './App.css'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
timer: 0,
};
this.timerInc = this.timerInc.bind(this);
}
timerInc() {
this.setState({
timer: 1,
})
}
render () {
return (
<div className="App">
<h1 onClick={this.timerInc}>Hello</h1>
<h1>{this.state.timer}</h1>
<h1>Hello</h1>
</div>
)};
}
export default App;
Related
I am making a very very simple nextjs application where I am trying to fetch the data from api.
My requirement is I should display the data in layout.js file and this layout.js file is a children in index.js file.
index.js:
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js:
import React from "react";
import fetch from "isomorphic-unfetch";
function Layout(props) {
return (
<div>
<p>Preact has {props.stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
Layout.getInitialProps = async () => {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
return { stars: json.stargazers_count };
};
export default Layout;
So as per the above given code, I have called the layout page inside index.js page (in my real application I need to call like this only so no changes in calling layout inside index)..
But when I made a console.log() in the function Layout.getInitialProps in layout, it doesn't print anything and hence the api data not fetched..
Complete working demo here with code
Why can't I fetch the data inside the layout.js while calling as a children from index.js?
Also provide me the right updated solution to achieve this.. I really searched for many questions but none solved my issue and I couldn't understand those solutions clearly so please help me with the above given example.
That because getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
You should use componentDidMount() or useEffect instead, or move getInitialProps in the index and then pass the result to the component. something like (not tested) :
index.js :
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js
import React from "react";
import fetch from "isomorphic-unfetch";
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
stars: false
};
}
async componentDidMount() {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
this.setState({ stars: json.stargazers_count });
}
render() {
const { stars } = this.state;
return (
<div>
<p>Preact has {stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
}
export default Layout;
Edit:
Example with class component
Bonus: If you want to add the layout for all the pages of your app this isn't the best approach, instead you should take a look to custom _app.js, example
I am trying to create a reusable stateful component (it shouldn't be functional component). I need to add this component in runtime so in my App i have an array of the component (CanvasComponent) in my state to render the list of component. I also generate a random size to render the size of canvas. The problem occurs when I create second canvas, Weirdly it is only render once.
I have this problem in ChartJS and since my code base is very big I decided to simplify it by a sample.
However if you uncomment CanvasComponent in the Array it works perfectly fine.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import CanvasComponent from './CanvasComponent';
class App extends React.Component {
state = {
canvasList: [
// <CanvasComponent size={30}></CanvasComponent>,
// <CanvasComponent size={50}></CanvasComponent>
]
}
handleClick = () => {
const size = Math.floor(Math.random() * (100 - 50 + 1) + 50);
const newCanvas = <CanvasComponent size={size}></CanvasComponent>
this.setState({
canvasList: [newCanvas,
...this.state.canvasList]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Add canvas</button>
{ this.state.canvasList.map((item, i) => {
return <CanvasComponent {...item.props} key={i}></CanvasComponent>
})}
</div>
);
}
}
export default App;
And the component
import React from 'react'
class CanvasComponent extends React.Component {
constructor(props) {
super(props);
this.myCanvas = React.createRef();
}
componentDidMount() {
const ctx = this.myCanvas.current.getContext('2d');
ctx.fillRect(0, 0, 100, 100);
}
render() {
console.log(this.props);
return (
<div>
<p>Size should be {this.props.size}</p>
<canvas ref={this.myCanvas} width={this.props.size} height={this.props.size} />
</div>
)
}
}
export default CanvasComponent
I believe that your issue here is that you're rendering the canvas components programmatically. If something was not present when the page first loaded, then event listeners are not actively looking for it.
I'm sure there's a more elegant solution than mine, but I tend to get around this issue by writing something like.
state={ updated: false}
componentDidMount(){
this.setState({updated:true})
}
Updating the state forces a rerender, and the event listeners will know to pay attention to the relevant component.
The issue was here, I will share here in case someone have had same issue, can find it.
Instead of
this.setState({
canvasList: [newCanvas,
...this.state.canvasList]
})
You should write
this.setState({
canvasList: [...this.state.canvasList,
newCanvas]
})
I still don't know why, but it fixed the problem.
I'm trying to build a random quote generator that loads A quote on componentDidMount with an axios api request , then loads new quotes on button click.
This is for A freecodecamp project. I have tried making the call again on button click, then adding the new response to state, but it will not work at all.
import React, { Component } from 'react'
import Button from './Button';
import axios from 'axios'
class QuoteBox extends Component{
constructor(props){
super(props)
this.state = {
quotes: []
}
}
componentDidMount(){
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=1')
.then(res=> this.setState({quotes: res.data[0]}))
}
getNext = (ev) =>{
ev.preventDefault()
axios.get('http://quotesondesign.com/wp-json/posts?
filter[orderby]=rand&filter[posts_per_page]=2')
.then(res=> this.setState({quotes:[...this.state,res.data[0]]}))
}
render(){
const {content,title} = this.state.quotes
const filteredContent = String(content).replace(/(<\w>)|(<\/\w>)|
(&#\d{4})/gm, "").replace(/(;)/g,"'")
console.log(content)
return(
<React.Fragment>
<h2>A little inspiration for the day</h2>
<div className='outerQuoteBox'>
<div className='innerQuoteBox'>
<p>{filteredContent}</p><br/><br/>{title}
</div>
<Button getNext={this.getNext} />
</div>
</React.Fragment>)
}
}
export default QuoteBox
And this is my button component
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button
When I click the button, it seems like the request isn't going through at all. If i check State in the dev tools, only the first quote from componentDidMount is in the array. I don't understand where my mistake is.
Edit: I had used the wrong prop reference, so it wasn't making the call. I fixed this and it does make the call now, and it brings in one new quote, but that's it. And it doesn't add the new one to state, it just replaces it with the one new one. and that's all it will do. The api instructions say the end point i'm using should return a new random quote, but it does not.
It looks like you're referencing the wrong prop on the button.
Change getQuote to getNext and it should work...
import React, { Component } from 'react'
export class Button extends Component {
render() {
return (
<React.Fragment>
<button onClick={this.props.getNext} className='nextBtn'
type='button'>Next</button>
</React.Fragment>
)
}
}
export default Button
I know there are a few questions out there exactly like this, but believe me none of them solved this problem. The main difference is that im tryng to update a nested component.
import React from 'react';
import Events from './Events.js'
import EventType from '../events/EventType.js'
import PropertiesPanelStyle from './css/PropertiesPanel.css';
import JointProperties from './properties/JointProperties.jsx';
export default class extends React.Component {
constructor(props) {
super(props);
this.state = {
name: "Properties",
visible: false,
display: undefined
}
Events.on(EventType.JOINT_CLICK, this.onJointClick.bind(this));
}
onJointClick(clickedJoint) {
console.log("CLICKED ",clickedJoint);
this.setState({
display: <JointProperties joint={clickedJoint}/>,
visible: true
});
}
render () {
if(!this.state.visible)
return (<div></div>);
return (<div>
<div id="properties">
<strong>{this.state.name}</strong>
<div id="propertiescontent" key={1}>
{ this.state.display }
</div>
</div>
</div>);
}
}
Whenever i click, i get a new joint as a parameter, and set the display to be the JointProperties to display a that joint. Whenever i do the first click, it displays correctly, but whenever i do the second click, it still displays the first joint (and the constructor of the JointProperties component is not called again)
Depending on what i click i will get different rendering, at least that was my initial idea. But idk if what am i missing here :(
I've been chasing this bug all day. I have a dead-simple React entry point, and a dead-simple component state change example component. If I put the component into the entry point, like this:
import React from 'react';
import { render } from 'react-dom';
export default class Template extends React.Component {
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.state = {
collapsed: true,
};
}
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<div>
<p>Collapsed: { this.state.collapsed ? 'true' : 'false' }</p>
<button onClick={this.toggleNavbar}>Toggle</button>
</div>
);
}
}
render(
<Template />,
document.querySelector('#react-app'),
);
It works as expected. You click the toggle button, and the text changes back and forth between 'true' and 'false'. However, the minute I break it out into two separate files, giving me this for the entry point:
import React from 'react';
import { render } from 'react-dom';
import Template from './components/Template';
render(
<Template />,
document.querySelector('#react-app'),
);
and this for Template.jsx
import React from 'react';
export default class Template extends React.Component {
constructor(props) {
super(props);
this.toggleNavbar = this.toggleNavbar.bind(this);
this.state = {
collapsed: true,
};
}
toggleNavbar() {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<div>
<p>Collapsed: { this.state.collapsed ? 'true' : 'false' }</p>
<button onClick={this.toggleNavbar}>Toggle</button>
</div>
);
}
}
Any time I click on the button I get the following error in the console:
build.js:23484 Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Template component.
... I've checked all of the other Stack Overflow answers for that error (and also searched around a ton), and none of them seem to be applicable here. Anyone have any idea what I'm doing wrong?
side note: I've tried adding:
componentWillUnmount() {
this.isUnmounted = true;
}
and a !this.isUnmounted check before setState() and I still get the error.
Thanks!
I found the issue: my .babelrc contained this line:
"plugins": ["react-hot-loader/babel"]
And it was conflicting with the rest of my webpack hot-reloading setup. Removing that line did the trick. I think what was happening is that the component was getting rendered, but somehow react was getting confused about what was/wasn't mounted (maybe it was very quickly getting mounted, unmounted, and then re-mounted, and so the bound toggleClick function was trying to set state on an old version of the component? Unsure).
Anyway, the moral of the story is: the React code is fine. It was a problem with my config.