The constructor and render function is called twice - reactjs

I have a very simple component, in that I log the information to check the Component Lifecycle and see that the constructor and render function is called twice every time when I reload the browser. Could anyone please help me to review why?
Here is my code, and the result in the picture.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
name: 'Viet'
};
console.log('App constructor');
}
componentWillMount() {
console.log('App componentWillMount');
}
componentDidMount() {
console.log('App componentDidMount');
}
changeState = () => {
this.setState({ name: 'Viet is changed' })
}
render() {
console.log('App render');
return (
<div className='App'>
{this.state.name}
{<button onClick={this.changeState.bind(this)} >Click to change state</button>}
</div>
);
}
}
export default App;

In your render method you are not passing the function right due to which component is re-rendered again. You need to bind the function in constructor change the onClick of button as:
constructor(props) {
super(props);
this.state = {
name: 'Viet'
};
console.log('App constructor');
this.changeState = this.changeState.bind(this);
}
<button onClick={this.changeState}>Click to change state</button>

The double rendering is due to React.StrictMode. If you check your src/index.js the App Component is wrapped with React.StrictMode tags.
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
As mentioned in the release notes:
"React.StrictMode is a wrapper to help prepare apps for async rendering"
P.S: There is nothing to worry about re-rendering.

Related

React componentDidUpdate() does not fire

I have an react app with primereact installed and I am using primereact/captcha.
Maybe I have misunderstood something, but isn't the following code supposed to work (console.log('Child component did update'))?
import React from 'react';
import { Captcha } from 'primereact/captcha';
export default function App() {
return (
<div className="App">
<ParentComponent/>
</div>
);
}
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
class ParentComponent extends React.Component {
constructor() {
super();
this.state = {
captchaSovled: false,
key : Math.random()
}
}
render() {
let output;
if (this.state.captchaSolved) {
output = <Child key={this.state.key} />;
} else {
output =<Captcha siteKey="xxxxxxx" onResponse={() => this.setState({ key : Math.random(), captchaSolved: true })} />
}
return (
<div>
<h1>Parent component</h1>
{output}
</div>
);
}
}
From React doc
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
In your code, the Child component is mounted after captchaSolved state is set, therefore only componentDidMount is fired on Child component.
componentDidUpdate is fired, if there is any change in the state or props. As of your component child:
class Child extends React.Component {
componentDidUpdate () {
console.log('Child component did update');
}
render() {
return (<h2>Child component</h2>);
}
}
There is no state or props which are changing, that's why componentDidUpdate never get's invoked.

React material UI open login dialog if token expired

I am using react material UI. I am frequently checking if token is expired using setInternval() and if its expire than login dialog should be open and setInterval should be cleared using clearInterval(). Below is my code but I am getting warning as 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. and not able to achieve the required result.
App.js
import AuthService from './includes/AuthService.js';
class App extends React.Component {
constructor(props) {
super(props);
this.Auth = new AuthService();
}
componentDidMount(){
setInterval(() => {this.Auth.checkToken()}, 10000);
}
}
AuthService.js
class AuthService extends React.Component{
constructor(props) {
super(props);
this.state = {email: '', password : '', loginOpen : false};
}
checkToken() {
console.log("token checked");
if (decode(localStorage.getItem('jwtToken')).exp < Date.now() / 1000) {
this.setState({loginOpen : true}, () => {
console.log('state updated');
console.log(this.state.loginOpen);
clearInterval();
});
}
}
render(){
const { onRequestClose } = this.props;
const actions = [
<FlatButton
label="Close"
primary={true}
onClick={this.handleClose}
/>,
];
return (
<MuiThemeProvider>
<Dialog title="Result Details"
actions={actions}
modal={false}
open={this.state.loginOpen}
onRequestClose={this.handleClose}
autoScrollBodyContent={true}
>
</Dialog>
</MuiThemeProvider>
}
loginOpen and checkToken() look like something that can be moved up to App component, and be passed to AuthService as props.
Alternatively, you can simply move down and call checkToken() on componentDidMount() function of AuthService.
Like this:
class AuthService extends React.Component{
constructor(props) {
super(props);
// ...
this.checkToken = this.checkToken.bind(this);
this.intervalId = null;
}
componentDidMount(){
this.intervalId = setInterval(() => {this.checkToken()}, 10000);
}
checkToken() {
console.log("token checked");
if (decode(localStorage.getItem('jwtToken')).exp < Date.now() / 1000) {
this.setState({loginOpen : true}, () => {
console.log('state updated');
console.log(this.state.loginOpen);
if (this.itv) {
clearInterval(this.intervalId);
}
});
}
}
See which approach will work better, and see if my fix works, and I can add more explanations.
One more thing I want to point out is that clearInterval(..) takes the ID returned from setInterval.
Hence the setting of this.intervalId and passing it to clearInterval(..).
From your comment:
how can I change state in parent component i.e. app component from its
child component. Becase login modal is in app component.
You are rendering login modal in App component.
You can conditionally render the login modal based on App's this.state.loginOpen.
For example, if your App render function contains a login modal component called LoginModal
render() {
<div>
{ this.state.loginOpen && <LoginModal /> }
</div>
Or, if you are calling some function to show the login modal, you can do something like if (this.state.loginOpen) { showLoginModal(); }.

Getting an arrow function syntax error in React

I have following code which is rendering the React app.
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import YTSearch from 'youtube-api-search';
import VideoList from './components/video_list'
const API_KEY = 'AIzaSyCF7K58Xwpr7m5C0yGy8Bck02iQ0fJ2yuI';
class App extends React.Component {
constructor(props){
super(props);
this.state = {videos: []};
this.YTSearch = this.YTSearch.bind(this);
}
YTSearch({key: API_KEY, term: BMW}, (videos => {
this.setState({ videos });
});
);
render() {
return (
<div>
<SearchBar />
<VideoList videos={ this.state.videos }/>
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector('.container'));
Also I think I have some syntax problem with using the setState function.
Class body is for defining functions and variables but you are calling the function YTSearch inside class body, which is giving syntax error. If you want to call the function then either call it inside constructor or inside any other function like componentDidMount etc
constructor(props){
super(props);
this.state = {videos: []};
}
componentDidMount(){
// Call it here inside componentDidMount or any other function
YTSearch({key: API_KEY, term: BMW}, (videos => {
this.setState({ videos });
}));
}
Your destructured setState is fine, you have a bracket (open which needs to be closed or either way you can remove it as there is only one argument in your arrow function.
Your specific issue isn't made clear in your question but from looking at your code I assume your YTSearch is never firing and therefore your state never gets set with a list of videos.
If you are trying to create a method to pass to the search bar that triggers a search perhaps try something like this. I hope this helps!
import React from 'react';
import ReactDOM from 'react-dom';
import SearchBar from './components/search_bar';
import YTSearch from 'youtube-api-search';
import VideoList from './components/video_list';
const API_KEY = 'AIzaSyCF7K58Xwpr7m5C0yGy8Bck02iQ0fJ2yuI';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { videos: [] };
this.search = this.search.bind(this);
}
search(phrase) {
YTSearch({ key: API_KEY, term: phrase }, videos => {
this.setState({ videos });
});
}
render() {
return (
<div>
<SearchBar onSearch={this.search}/>
<VideoList videos={this.state.videos} />
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector('.container'));

Property for react component is not defined

After running this code - I got the exception that "title" is not defined. I checked that api returns correct data. And on the debug mode I noticed that render() from Idea component is running earlier than getting the data from API. Can you explain why is it working in this way? And what options I have for resolving this issue?
Thanks
'use strict';
const React = require('react');
const ReactDOM = require('react-dom');
const client = require('./client');
class App extends React.Component {
constructor(props) {
super(props);
this.state = {map: {}};
}
componentDidMount() {
client({method: 'GET', path: '/api/maps/1'}).done(response => {
this.setState({map: response.entity._embedded.map});
});
}
render() {
return (
<Map map={this.state.map}/>
)
}
}
class Map extends React.Component {
render() {
return (
<div id="map_header">
<AddIdeaButton></AddIdeaButton>
<Idea idea={this.props.map.root}></Idea>
</div>
);
}
}
class AddIdeaButton extends React.Component {
render() {
return (
<a id="btn_add">
</a>
);
}
}
class Idea extends React.Component {
render() {
<div id="root">{this.props.idea.title}</div>
}
}
ReactDOM.render(
<App />,
document.getElementById('react')
);
Asynchronous request for data takes some time during which React still renders Map and Idea components. You can simply render Idea conditionally when data is available:
<div id="map_header">
<AddIdeaButton></AddIdeaButton>
{this.props.map.root && (
<Idea idea={this.props.map.root}></Idea>
)}
</div>

forceUpdate is not re-rendering children

I'm using the react, redux react-router stack for my webapp. In the top level component's(the component that renders on the root path) componentDidMount I'm subscribing to the store as shown below
import NotificationsList from './components/notifier';
import React from 'react';
let Spinner = ({
isVisible,
showSpinner,
solidBackdrop
}) => (
<div style={{opacity: solidBackdrop ? 1 : 0.5}} className={"spinner " + (isVisible ? '' : 'hide')}></div>
);
export default class AppPage extends React.Component {
static contextTypes = {
store: React.PropTypes.object,
router: React.PropTypes.object
};
handleDismissNotification(notification) {
this.context.store.dispatch({
type: 'REMOVE_NOTIFICATION',
data: notification
});
}
componentDidMount() {
this.context.store.subscribe(() => this.forceUpdate());
}
render() {
let state = this.context.store.getState();
let props = {
notifications: state.notifications,
handleDismiss: this.handleDismissNotification.bind(this)
};
return (
<div className="'apppage-container">
{this.props.children}
<NotificationsList {...props} />
<Spinner isVisible={state.initialFetchInProgress || state.requestInProgress}
showSpinner={!state.initialFetchInProgress} solidBackdrop={state.initialFetchInProgress}/>
</div>
);
}
}
this.props.children here renders the component shown below
import Header from './components/header';
import React from 'react';
class ContentPage extends React.Component {
static contextTypes = {
store: React.PropTypes.object
};
render() {
let user = this.context.store.getState().user;
return <div className="content-container">
<Header user/>
</div>
}
}
export default ContentPage;
The problem is that when the first time a render happens, everything goes fine. Then when the render happens through forceUpdate, the child component is not getting re-rendered.
I think I got it. Every container component should be subscribed to the store separately. So accordingly, ContentPage should also have
componentDidMount() {
this.context.store.subscribe(() => this.forceUpdate());
}
As you replied to yourself, indeed the container component should subscribe to the store , but in addition to the subscription, it's good practice for the the container to also unsubscribe when unmounted :
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => this.forceUpdate());
}
componentWillUnmount() {
this.unsubscribe();
}

Resources