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/
Related
I am working on a simple application with routes.
I have defined 2 routes as below:
App.js
import React from 'react';
import { Route, Routes, BrowserRouter } from 'react-router-dom';
import './App.css';
import Route1 from './components/Route1';
import Route2 from './components/Route2';
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('This is from App.js')
}
render() {
<BrowserRouter>
<Routes>
<Route path="/route1" element={[<Route1]}/>
<Route path="/route2" element={[<Route2]}/>
</Routes>
</BrowserRouter>
}
}
export default App;
Route 1
import React, { Component, createRef } from 'react'
class Route1 extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
Console.log("This is from Route1")
// some code
}
render() {
return()
}
export default Route1
Route 2
import React, { Component, createRef } from 'react'
class Route2 extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
Console.log("This is from Route2")
// some code
}
render() {
return()
}
export default Route1
I want to execute code written in app.js first, even before routes.
When I am on localhost:3000/route1, I can console output as below:
This is from Route1
This is from App.js
But I want to execute the app.js code before any routes, so expected putout is:
This is from App.js
This is from Route1
Writing your code in constructor will do your job. It should look like this :
constructor(props) {
super(props);
console.log('This is from App.js')
}
The App component needs to completely mount/render its sub-ReactTree prior to itself being considered mounted/rendered. If there is some logic you want to run prior to children being rendered then you might need to add some "initial render" state to the parent and conditionally render the children once the parent component has mounted and completed the logic.
Example:
class App extends React.Component {
state = {
loaded: false,
}
componentDidMount() {
console.log('This is from App.js');
this.setState({ loaded: true });
}
render() {
const { loaded } = this.state;
return (
<BrowserRouter>
{loaded && (
<Routes>
<Route path="/route1" element={<Route1 />} />
<Route path="/route2" element={<Route2 />} />
</Routes>
)}
</BrowserRouter>
);
}
}
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);
};
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
I have a situation where I need to fetch updated props within componentWillMount()
My Layout :
#connect((store) => {
//console.log(store);
return {
element: store.elements.elements,
connections: store.connections.connections,
attributes: store.attributes.attributes,
media: store.media.media,
places: store.places.places,
user: store.user.user
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchUser())
}
componentWillReceiveProps(nextProps) {
this.props.dispatch(updateStoreUser(nextProps.user))
}
shouldComponentUpdate(nextProps) {
console.log(nextProps);
return true;
}
render() {
const { location } = this.props;
return (
<div className="main-container">
<Header/>
<NavConnector/>
{this.props.children}
</div>
);
}
}
{this.props.children} will render pages depending on the route.
I have a BasicInfo Component :
componentWillMount() {
console.log(this.props);
this.props.dispatch(fetchPlaces(1))
}
Where I need to pass user id to fetchPlaces, something like this this.props.dispatch(fetchPlaces(this.props.user.id)
But this.props does not have user.id yet, in the componentWillReceiveProps of the layout I'm updating the store, but gets updated after componentWillMount() of BasicInfo component is called.
The console log :
UPDATE
I have a connector for BasicInfo, this.props.user inside the render method is always undefined. But the store has the user values by now.
Is there any way to pass data from Layout? The place where {this.props.children} is being called? Because that's where the BasicInfoConnector is being called.
import React from "react"
import * as Redux from 'react-redux';
import Basicinfo from "./Basicinfo"
const mapStateToProps = function (store) {
return {
elements: store.elements.elements,
places: store.places.places,
geocode : store.geocode.geocode,
user : store.user.user
};
};
class BasicinfoConnector extends React.Component{
render() {
console.log(this.props.user);
return (
<BasicInfoConnector elements={this.props.elements} places={this.props.places} geocode={this.props.geocode} user={this.props.user}/>
);
}
}
export default Redux.connect(mapStateToProps)(BasicinfoConnector);
Client JS
import React from "react"
import ReactDOM from "react-dom"
import { Router, Route, IndexRoute, hashHistory } from "react-router"
import { Provider } from "react-redux"
import { useScrollToTop } from "scroll-behavior"
import store from "./store"
import '../styles/sass/master/global.scss'
import Layout from "./components/Layout";
import Alerts from "./components/Dashboard/Alerts/Alerts"
import AttributesConnector from "./components/Dashboard/Attributes/AttributesConnector"
import BasicInfoConnector from "./components/Dashboard/Basicinfo/BasicinfoConnector"
import ConnectionsConnector from "./components/Dashboard/Connections/ConnectionsConnector"
import MediaConnector from "./components/Dashboard/Media/MediaConnector"
import Stats from "./components/Dashboard/Stats/Stats"
const app = document.getElementById('app')
ReactDOM.render(
<Provider store={store}>
<Router histroy={hashHistory} onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" component={Layout}>
<IndexRoute component={BasicInfoConnector}></IndexRoute>
<Route path="location" component={BasicInfoConnector}></Route>
<Route path="alerts" component={Alerts}></Route>
<Route path="attributes" component={AttributesConnector}></Route>
<Route path="connections" component={ConnectionsConnector}></Route>
<Route path="media" component={MediaConnector}></Route>
<Route path="stats" component={Stats}></Route>
</Route>
</Router>
</Provider>,
app);
Assuming that you want to fetch places in componentWillMount, the only solution is to not render the component at all using conditional rendering until the user id is available since componentWillMount gets called only once. Something like this:
{this.props.user?<BasicInfo />:null}
Update:
You need to export a component which is connected (subscribed) to redux store. You are exporting the component which is not connected yet. Just remove the export default before the component declaration
class BasicinfoConnector extends React.Component
and add an export default before the connect statement.
export default Redux.connect(mapStateToProps)(BasicinfoConnector);
This should fix your issue.