In my reactjs project, component is rendered twice. if I remove the componentDidMount, the problem is fixed. But I need it in my project. I tried the solutions on the internet, but I couldn't.
App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
fetch("https://reqres.in/api/users?page=2")
.then(res => res.json())
.then(result => {
this.setState({
users: result.data
});
});
}
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" render={() => <Home />} />
</Switch>
</BrowserRouter>
);
}
}
Home.js
export default class Home extends Component {
render() {
console.log("Render");
return (
<div>
<h1>console.log render twice</h1>
</div>
);
}
}
https://codesandbox.io/s/wyjk931z6l
console.log works on Home.js twice.
You App component re-renders because the API call that you make in componentDidMount results in a setState on success. Due to this, the child components also go though a re-render even though their props didn't change. In order to avoid it, you can write the child component by extending React.PureComponent which implements the shouldComponentUpdate by shallowly comparing the props and state.
export default class Home extends PureComponent {
render() {
console.log("Render");
return (
<div>
<h1>console.log render twice</h1>
</div>
);
}
}
Working demo
Related
I need some help to solve the following issue with using React.
In some web app I have a landing page, where I want to redirect the user to the login page in case she or he is not logged in.
I want to use the following landing page (taken from some tutorial I found on the net) in order to use it as a model for mine.
The problem is that this is a function component while my landing page is a class component. According to what I understand I guess I need to consider the code inside useEffect and (somewhat) transfer it to componentDidMount() in my class component. But I don't know how to do that. history.replace will not work in a class component (no Hooks in Classes). Any advice from a more React experienced user will be very welcome.
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { useHistory } from "react-router";
import "./Dashboard.css";
import { auth, db, logout } from "./firebase";
....
function Dashboard() {
const [user, loading, error] = useAuthState(auth);
const [name, setName] = useState("");
const history = useHistory();
....
useEffect(() => { // Important part for my question !
if (loading) return;
if (!user) return history.replace("/");
....
}, [user, loading]);
return (
<div>
{/*...*/}
<button className="dashboard__btn" onClick={logout}>
Logout
</button>
</div>
);
}
export default Dashboard;
Here is what I tried on my Class Component:
class MyCompo extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("--componentDidMount(MyCompo)--");
const { history } = this.props
history.push("/login");
}
.....
}
But I get the following error:
TypeError: history is undefined
componentDidMount
=============== Added information ===============
Below is the relevant part of the code I have been working on:
This part is what works:
<Route exact path="/" component={TopMenu}>
{true && <Redirect to="/login" />}
</Route>
What I tried in the Links Component did not work.
The code:
....
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/" component={TopMenu}>
{true && <Redirect to="/login" />}
</Route>
<Route exact path="/login" component={Login} />
<Route exact path="/section1" component={Section1Page}/>
<Route exact path="/section2" component={Section2Page}/>
<Route exact path="/section3" component={Section3Page}/>
</Switch>
</Router>,
document.getElementById('root')
);
....
const TopMenu = () => {
return (
<div className='page_container'>
<Title/>
<Links path='/'/>
<button className="dashboard__btn" onClick={logout}>
Logout
</button>
</div>
)
};
class Links extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log("--componentDidMount(Links)--");
// This is some code I tried with no success.
const { history } = this.props
//history.push("/login");
}
componentDidUpdate(prevProps, prevState) {
console.log("--componentDidUpdate(Links)--");
}
render() {
return (
<div className='links_container'>
{(this.props.path != '/mng') &&
<React.StrictMode>
<Link to='/mng'>{mnMgrStr()}</Link><br/>
</React.StrictMode>}
{(this.props.path != '/other') &&
<React.StrictMode>
<Link to='/other'>{otherInpStr()}</Link><br/>
</React.StrictMode>}
.......
</div>
)
}
}
Following the example on the React Router docs you can use withRouter if your component isn't already receiving the route props, otherwise you can access history from the props.
class MyComponent extends React.Component {
...
componentDidMount() {
const { history } = this.props
// do whatever with history here
}
...
}
In react-router-dom version 5 there are a couple ways a class component can access the history object.
Rendered directly by a Route component via the component, or render or children function props so route props (i.e. history, location, and match) are passed.
component: <Route path="....." component={MyCompo} />
render: <Route path="....." render={routeProps => <MyCompo {...routeProps} />} />
Access the history object from the passed route props:
class MyCompo extends React.Component {
componentDidMount() {
const { history } = this.props;
history.push("/login");
}
...
}
Decorated by the withRouter Higher Order Component so the route props are injected.
import { withRouter } from 'react-router-dom';
class MyCompo extends React.Component {
componentDidMount() {
const { history } = this.props;
history.push("/login");
}
...
}
export default withRouter(MyCompo);
Well I hope by answering this question I can save lot of time of others. Don't need to panic it's not a major issue. I will explain step by step reason and solution.
First of all why this happening is
In react-router-dom **V6 (version 6) latest ** there is no history export or redirect.
There is navigate construct.
So to achieve in functional component there is useNavigate() hook.
Now coming to answer...
To redirect in class component using react-router-dom V6 we have to use component.
So now one has to follow the following steps:
Import navigate
import { Navigate } from "react-router-dom";
Use Navigate to redirect
So above I discussed syntax to do so now coming to your exact problem
You have to redirect user to login if he is not logged in
You can follow these steps:
create state to store status of user like logged in or not (inside constructor of class)
this.state = {
userLogged: false,
};
in your render method you have to add some condition like if user is not logged in take user to login page. see below..
render() {
const { userLogged } = this.state;
if (goToPay) {
return (
<Navigate to="/cart" state={selectedTiffin} props={selectedTiffin} />
);
}
}
That's it.
It can be confusing so I am giving full example so you can save your lot of time..
import React from "react";
import { Navigate } from "react-router-dom";
class Solve extends React.Component {
constructor(props) {
super(props);
this.state = {
userLogged: false,
};
}
// here you can write code to set the status of user like logged in or not
render() {
const { userLogged } = this.state;
if (userLogged ) {
return (
<Navigate to="/cart" />
);
}
return (
<>
Here you can return your original component that should be render when user is log in
</>
);
}
}
I hope this will help and work. Thank You
I have a component (SearchFilter.js) and am using connect to trigger mapStateToProps and mapActionsToProps on export.
Trouble is, mapStateToProps isn't firing -- no props (neither state nor actions) show up in React DevTools and I can't even console log from inside mapStateToProps.
I've tried looking at various Stack Overflow threads but they mostly seem to be typos, or the actions themselves not working.
What's more, I've got an almost identical redux setup for another component (Counter.js) that woks perfectly.
I think it could have something to do with how I provide the store/route to components (see App.js below) as React.Provider shows up in React DevTools for the Counter but not SearchFilter.
Here's the SearchFilter component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { addSearchTerm } from "../redux/actions/searchActions";
import "../styles/SearchFilter.css";
export class SearchFilter extends Component {
constructor(props) {
super(props);
this.state = {
searchTerm: "",
showFilters: false,
};
}
//various content...
}
const mapStateToProps = (state) => {
console.log(state);
return {
search: state.search,
};
};
const mapActionsToProps = {
addSearchTerm,
};
export default connect(mapStateToProps, mapActionsToProps)(SearchFilter);
App.js
function App() {
return (
<Router>
<Provider store={store}>
<div className="App">
<NavBar />
<Counter />
<Switch>
<Route exact path="/" component={Home} /> // SearchFilter rendered in Home page
<Route path="/account" component={Account} />
</Switch>
</div>
</Provider>
</Router>
);
}
EDIT: Where I've implemented this component in the Home.js view:
export default function Home() {
return (
<div>
<h4>This is the Home page</h4>
<SearchFilter />
<ProfilesList />
</div>
);
}
Try to remove the "export" when you declare the class component, maybe that helps.
change
export class SearchFilter extends Component
to
class SearchFilter extends Component
Try using bindActionCreators from redux library - https://redux.js.org/api/bindactioncreators
To dispatch any action from your component, update mapActionsToProps const in your SearchFilter component.
const mapActionsToProps = (dispatch) => bindActionCreators({
addSearchTerm},dispatch);
};
I build a project for a school’s records system, in which I build the front-end with React. On the main component of the admin page, I wanted to have a react-router which will navigate through the admin dialogs. As I tried to implement this, the following problem occurred: when trying to pass parameters to a class through the react route component, the child component receives no props.
I have the following react component hierarchy:
class Test extends React.Component {
constructor() {
super();
console.log("in class: " + this.props)
}
render() { return <div>test</div>}
}
class AdminPage extends BasicPage {
/* Other class functions here... */
render() {
let pageBody = "";
if(!this.state.isLoading)
pageBody = (
<Router>
<Switch>
<Route path={"/:schoolName/admin"} component={AdminMenu} exact/>
<Route path={"/:schoolName/admin/view/:id"} exact
component={() => <Test par1="abc" />} />
</Switch>
</Router>
);
return (
<Layout title={ this.state.isLoading ?
TITLE_UNTIL_LOADED :
PAGE_TITLE + this.state.schoolPrefs.heb_name}
subtitle={ this.state.subtitle }
notification={ this.state.notification }
isLoading={ this.state.isLoading }
>
{pageBody}
</Layout>
);
}
}
When I go to /Random Name/admin/view/someID, it prints to the console in class: undefined.
I then wanted to see if the problem is in the passing component or the receiving one. I defined the function otherTest(props) as follows:
function otherTest(props) {
console.log("Function props: " + props);
return (<Test {...props} />);
}
And then changed the route component like so:
<Route path={"/:schoolName/admin/view/:id"} exact
component={otherTest} />
When then I went to /Random Name/admin/view/someID, I saw that the function received the props just fine, but the log within <Test … /> still printed undefined.
I also tried adding <Test param1=”123” /> after the {pageBody} variable in the main render function, but it printed in class: undefined as well.
Does someone know where the problem might be?
Thanks.
You must take props parameter from constructor and then pass it to super.
constructor(props){
super(props);
}
Do not use this.props in constructor beacuse constructor fire only at the creating class moment.
Use this code:
constructor(props) {
super(props);
console.log(props);
}
I'm currently creating a timer application in react. I have a dashboard where the user can control the timer and now I want a different window where only the component rendering the time is being displayed so it can captured by recording programs.
I've tried the following:
In my app.js
function MasterStopwatch(props) {
return <span>{props.time}</span>
}
export { App, MasterStopwatch };
I'm passing in the props.time in the app.js
In my index.js
import { App, MasterStopwatch } from './App';
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={App}/>
<Route path="/master" component={MasterStopwatch} />
</Router>
, document.getElementById('root')
);
But when I visit the master route it displays nothing and doesn't show an error in the console.
Any help would be appreciated!
As per your route MasterStopwatch should be child component of App.
if you are passing props to MasterStopwatch you need to pass it from parentComponent App.
export default class App extends React.component {
....
....
render(){
return(
<div>
<MasterStopwatch {...props}/>
</div>
)
}
}
export function MasterStopwatch(props) {
return <span>{props.time}</span>
}
Change your App and MasterStopWatch as follows:
export default class App extends React.Component {
render() {
return React.cloneElement(
this.props.children,
{time: this.props.time}
);
}
}
export default class MasterStopWatch extends React.Component {
render() {
return <span>{this.props.time}</span>
}
}
Source: http://javascript.tutorialhorizon.com/2015/11/02/pass-props-to-handler-component-in-react-router/
I'm trying to figure out how to access the redux store from within route so I can dispatch actions from within the route.
Here's what my top level Component looks like:
class App extends Component {
render() {
return (
<div>
{ children }
</div>
);
}
}
My redux-simple-router code looks like:
render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={ Home } />
<Route path="/example" component={ ExampleRoute } />
</Route>
</Router>
</Provider>,
rootElement
)
If I dump props from within the ExampleRoute component, I don't have access to the store. Any help appreciated!
You should use connect from react-redux to get dispatch and current state from the store. It is outlined in the redux docs here: http://rackt.org/redux/docs/basics/UsageWithReact.html
Here is your Example component:
//...
import { connect } from 'react-redux'
//...
export class Example extends Component {
render () {
const { dispatch, thingName } = this.props
return (
<button onClick={ () => {
dispatch(myAwesomeActionCreator())
}}>{ thingName }</button>
);
}
}
export default connect(state => state)(Example)
Some good examples of how to use connect can be found in the react-redux docs: https://github.com/rackt/react-redux/blob/master/docs/api.md#examples
I was able to get this working with "Monkeypatch" middleware, but there's got to be a better way.
First I created a function to monkeypatch the children variable. This function takes the child, the dispatch and the store as arguments, and returns an updated children variable with keys for the store and dispatch:
function routeStoreMiddleware (children, dispatch, store) {
return {
...children,
props: {
...children.props,
dispatch: dispatch,
store: store
}
}
}
Then I simply updated the component that already has access to the dispatch and store to consume the middleware function:
class App extends Component {
render() {
return (
<div>
{ routeStoreMiddleware(children, dispatch, store) }
</div>
);
}
}
Since the poorly named routeStoreMiddleware function simply returns an updated children object, it still works.
Now I can dispatch events and display data from within the ExampleRoute component.
import React, { Component } from 'react';
import { myAwesomeActionCreator } from '../actions.js'
export class Example extends Component {
render () {
const { dispatch, store } = this.props
return (
<button onClick={ () => {
dispatch(myAwesomeActionCreator())
}}>{ store.thingName }</button>
);
}
}
Yay!
Please note:
I've been reading a lot here about how to make middleware properly in redux, but I haven't had time yet to understand it fully. There's a better way than I've done here.