Reputation: 817
The constructor of my react component is getting called twice but I can't figure out why. I am using react-redux to store the language of my app. I have a function that set the default language base on browser's language. LoginPage is the first component that gets render so I called my function in its constructor. Basically what it does is compare and dispatch an action. When I checked my state with redux developer tools I saw that it was getting dispatched twice. I printed dummy data in the constructor and it gets printed twice too.
LoginPage.js
import React from 'react';
import {connect} from 'react-redux';
import {startLogin} from '../actions/auth';
import {setLanguage} from '../actions/lang';
export class LoginPage extends React.Component{
constructor(props){
super(props);
this.setDefaultLanguage();
console.log('i am constructor');
}
changeLanguage = (e) => {
const lan = e.target.value;
this.props.setLanguage(lan);
};
setDefaultLanguage = () => {
const defaultLanguage = navigator.language || navigator.userLanguage || 'en-US';
if(defaultLanguage == 'es'){
this.props.setLanguage(defaultLanguage);
}else{
this.props.setLanguage('en');
}
}
render(){
return(
<div className="box-layout">
<div className="box-layout__box">
<h1 className="box-layout__title">Expensify</h1>
<p>It\'s time to get your expenses under control.</p>
<button className="button" onClick={this.props.startLogin}>Log in with google</button>
<select className="select" onChange={this.changeLanguage}>
<option value="en">English</option>
<option value="es">Español</option>
</select>
</div>
</div>
)
};
}
const mapDispatchToProps = (dispatch) => ({
startLogin: () => dispatch(startLogin()),
setLanguage: (language) => dispatch(setLanguage(language))
});
export default connect(undefined, mapDispatchToProps)( LoginPage);
App.js
import React from 'react';
import ReactDom from 'react-dom';
import {Router, Route, Switch} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import ExpenseDashBoardPage from '../components/ExpenseDashBoardPage';
import AddExpensePage from '../components/AddExpensePage';
import EditExpensePage from '../components/EditExpensePage';
import NotFoundPage from '../components/NotFoundPage';
import LoginPage from '../components/LoginPage';
import PrivateRoute from './PrivateRoute';
import PublicRoute from './PublicRoute';
export const history = createHistory();
const AppRouter = () => (
<Router history={history}>
<div>
<Switch>
<PublicRoute path="/" component={LoginPage} exact={true} />
<PrivateRoute path="/dashboard" component={ExpenseDashBoardPage} />
<PrivateRoute path="/create" component={AddExpensePage} />
<PrivateRoute path="/edit/:id" component={EditExpensePage} />
<Route component={NotFoundPage} />
</Switch>
</div>
</Router>
)
export default AppRouter;
Upvotes: 15
Views: 17033
Reputation: 1816
Development mode can be disabled using the following code in index.js
// <React.StrictMode>
<Application />
// </React.StrictMode>
Upvotes: 1
Reputation: 131
Solution : Replace "React.StrictMode" by "<>" in your index.js
**Reason: ** It is important that state and lifecycle methods do not contains side effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be non-deterministic.
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
This only applies to development mode. Lifecycles will not be double-invoked in production mode.
Upvotes: 2
Reputation: 109
Yep. It’s intentional and documented here. It only happens in development mode.
https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
Upvotes: 4
Reputation: 91
This twice calling some of the lifecycle methods, is only for development mode.
"Lifecycles will not be double-invoked in production mode."
Upvotes: 4
Reputation: 81
I was having the same issue because I was redirecting the user inside my public routes. So it was rendering the component twice. In case you are using
history.push('/');
This is going to render the component twice.
Upvotes: 7
Reputation: 9918
First of all, you should not call Redux actions or any kind of AJAX from within the constructor. Those things should be done in componentDidMount()
.
Second, I would request the language from the store as part of props. If not defined, then in componentDidMount()
call your setDefaultLanguage()
.
I would do at least the following:
export class LoginPage extends React.Component {
componentDidMount() {
if (!this.props.lang) {
this.setDefaultLanguage();
}
}
changeLanguage = (e) => {
const lan = e.target.value;
this.props.setLanguage(lan);
};
setDefaultLanguage = () => {
const defaultLanguage = navigator.language || navigator.userLanguage || 'en-US';
if(defaultLanguage == 'es'){
this.props.setLanguage(defaultLanguage);
}else{
this.props.setLanguage('en');
}
}
render() {
return(
<div className="box-layout">
<div className="box-layout__box">
<h1 className="box-layout__title">Expensify</h1>
<p>It\'s time to get your expenses under control.</p>
<button className="button" onClick={this.props.startLogin}>Log in with google</button>
<select className="select" onChange={this.changeLanguage}>
<option value="en">English</option>
<option value="es">Español</option>
</select>
</div>
</div>
)
};
}
const mapStateToProps = state => ({
// Assuming `steate.lang` is where you would set the language.
lang: state.lang
});
const mapDispatchToProps = (dispatch) => ({
startLogin: () => dispatch(startLogin()),
setLanguage: (language) => dispatch(setLanguage(language))
});
const mergeProps = (stateProps, dispatchProps) => ({ ...stateProps, ...dispatchProps });
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(LoginPage);
Upvotes: 7
Reputation: 175
If you check React Dev Tools of what is being rendered you'll see something like connect(App)
as well as App
by itself, so your console.log will be called twice. This is because connect is a higher order component.
If you move this.setDefaultLanguage()
(and your console.log
so you can see it in action) out of the constructor and into the componentDidMount
life cycle hook, it should fix your problem.
Upvotes: 3