Seeing issues while developing a real time wallboard using react . I am displaying 2 components one after another Dashboard1( a table with data), Dashboard2(another table with data) with 3 secs time interval. My parent component is Dashboard it connects to my firestore DB to receive real-time updates and sends this data to Dashboard1 component and then Dashboard 1 renders its data and after 3 seconds calls Dashboard2 with the same data passed to it by Dashboard using props.history.push().I am seeing 2 issues here. The component Dashboard 2 is always rendered above Dashboard1.Like when i scroll down the page ,i can still see Dashboard1 at the bottom. How to clear off the page before rendering Dashboard1 and 2 .So that i just see a single component at a time on the screen.Below is my code for App, Dashboard ,Dashboard1 and Dashboard2.I am also seeing Dashboard 2 is being rendered multiple times in the console logs.
Kindly help me to fix these 2 issues:
App.js:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom'
import Dashboard from './components/Dashboard'
import Dashboard1 from './components/Dashboard1'
import Dashboard2 from './components/Dashboard2'
class App extends Component {
render() {
console.log('App')
return (
<BrowserRouter>
<div className="App">
<Route exact path='/' component={Dashboard} />
<Route exact path='/Dashboard1' component={Dashboard1} />
<Route exact path='/Dashboard2' component={Dashboard2} />
<Dashboard />
</div>
</BrowserRouter>
)
}
}
export default(App)
Dashboard.js:
import React, { Component } from 'react';
import { BrowserRouter, Route, Redirect } from 'react-router-dom'
import Dashboard1 from './Dashboard1'
import Dashboard2 from './Dashboard2'
import { connect } from 'react-redux'
import { firestoreConnect } from 'react-redux-firebase'
import { compose } from 'redux'
class Dasboard extends Component {
render() {
console.log('Dashboard')
const { agents } = this.props
if (!agents) {
return null
}
return (
<Dashboard1 data={agents}/>
)
}
}
const mapStateToProps = (state) => {
return {
agents: state.firestore.data.agent_groups
}
}
export default compose(
connect(mapStateToProps),
firestoreConnect([
{ collection: 'agent_groups' }
])
)(Dasboard)
Dashboard1.js:
import React from 'react'
import Table from '../layouts/Table'
import { withRouter } from 'react-router-dom'
const Dashboard1 = (props) => {
console.log('Dashboard1')
setTimeout(() => {
props.history.push({
pathname: '/Dashboard2',
state: { data: props.data.WFM_Inbound_Sales.agents }})
}, 3000);
return (
<div className="dashboard1">
<Table
data={props.data.WFM_Inbound_Sales.agents}
headers={[
{
name: 'Agent',
prop: 'name'
},
{
name: 'Total calls',
prop: 'InboundCalls'
}
]}
/>
</div>
)
}
export default withRouter(Dashboard1)
Dashboard2.js:
import React from 'react'
import Table from '../layouts/Table'
import { withRouter } from 'react-router-dom'
const Dashboard2 = (props) => {
console.log('Dashboard2')
setTimeout(() => {
props.history.push('/')
}, 3000);
return (
<div className="dashboard2">
<Table
data={props.location.state.data}
headers={[
{
name: 'Agent',
prop: 'name'
},
{
name: 'Status',
prop: 'state'
},
{
name: 'Time in status',
prop: 'TimeInCurrentState'
}
]}
/>
</div>
)
}
export default withRouter(Dashboard2)
You need to use switch which will render only one route of the given set like this
class App extends Component {
render() {
console.log('App')
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route exact path='/' component={Dashboard} />
<Route exact path='/Dashboard1' component={Dashboard1} />
<Route exact path='/Dashboard2' component={Dashboard2} />
</Switch>
<Dashboard />
</div>
</BrowserRouter>
)
}
}
Check the doc here
Related
I am trying to make a single web application. Basically, I am trying to use the ReactRouter to display what is passed as a Route Parameter. However, I am unable to do that. To check if somethings wrong, I decided to console.log out this.props.match, still nothing shows up. Could someone explain what the problem is? And a possible get around?
My code is-
import React from 'react';
export default class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
The App.js file:
import React, { Fragment, Component } from 'react';
import Navbar from './components/Navbar';
import Home from './components/Home';
import Contact from './components/Contact';
import About from './components/About'
import Post from './components/Post';
import { BrowserRouter, Route } from 'react-router-dom';
class App extends Component {
render = () => {
return (
<BrowserRouter>
<div className="App">
<Navbar />
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
<Route path="/about" component={About} />
<Route path="/:post-id" component = {Post} />
</div>
</BrowserRouter>
);
}
}
export default App;
I just ran your code on my end, it looks like the problem is using /:post-id. I changed that to /:pid and it worked. I got the below object when I console log this.props.match
{
"path":"/:pid",
"url":"/1",
"isExact":true,
"params":
{
"pid":"1"
}
}
I hope this helps.
You have to load the component with router
try this
import { withRouter } from 'react-router-dom';
class Post extends React.Component {
state = {
id: null
}
componentDidMount(props) {
console.log(this.props.match);
}
render = () => {
return (<div>Hello WOrld</div>)
}
}
export default withRouter(Post);
I'm trying to update my child component's photos from the parent components state. For all the other routes, the appropriate function was already invoked once the app was mounted. The component that renders cats, dogs, or computers is PhotoList.js
But now, I want to be able to enter a parameter after search (ex. /search/:id) and run a function called getImages in my Container.js to search for any type of picture from the Flickr API.
I tried using componentDidMount and invoking the getImages function with the match parameter inside of it but it doesn't seem to change the data props that's put into it. Does anyone have any suggestions as to how I can make this?
Here is Container.js
import React, {Component} from 'react';
import Photo from './Photo';
class Container extends Component {
componentDidMount() {
this.props.getImages(this.props.match.id)
}
render() {
return (
<div className="photo-container">
<h2>Results</h2>
<ul>
{this.props.data.map((photo,index)=>
<Photo
farm={photo.farm}
server={photo.server}
id={photo.id}
secret={photo.secret}
key={index}
/>
)}
</ul>
</div>
);
}
}
export default Container
Here is PhotoList.js
import React, {Component} from 'react';
import Photo from './Photo';
import NoResults from './NoResults';
class PhotoList extends Component {
render() {
return (
<div className="photo-container">
<h2>Results</h2>
<ul>
{this.props.data.map((photo,index)=>
<Photo
farm={photo.farm}
server={photo.server}
id={photo.id}
secret={photo.secret}
key={index}
/>
)}
</ul>
</div>
);
}
}
export default PhotoList;
Here is App.js
import React, {Component} from 'react';
import {
BrowserRouter,
Route,
Switch,
Redirect
} from 'react-router-dom';
import Search from './Search';
import Nav from './Nav';
import '../index.css';
import axios from 'axios';
import apiKey from './Config';
import NotFound from './NotFound';
import PhotoList from './PhotoList';
import NoResults from './NoResults';
import Container from './Container';
class App extends Component {
state= {
cats: [],
dogs: [],
computers: [],
searchResult: [],
loading: true
}
componentDidMount() {
this.getCats()
this.getDogs()
this.getComputers()
}
getCats=(query='cats')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const cats=res.data.photos.photo
this.setState({cats})
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getDogs=(query='dogs')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const dogs=res.data.photos.photo
this.setState({dogs})
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getComputers=(query='computers')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const computers=res.data.photos.photo
this.setState({computers});
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
getImages=(query)=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then (res=> {
const searchResult=res.data.photos.photo
this.setState({searchResult});
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
render() {
return (
<div className="container">
<Search getImages={this.getImages}/>
<Nav />
<Switch>
<Route exact path="/" render={()=> <Redirect to={'/cats'} />} />
<Route path='/cats' render={()=> <PhotoList data={this.state.cats}/>} />
<Route path='/dogs' render={()=> <PhotoList data={this.state.dogs} />} />
<Route exact path='/computers' render={()=> <PhotoList data={this.state.computers} />} />
<Route path='/search/:id' render={(props)=> <Container {...props} getImages={this.getImages} data={this.state.searchResult} />} />
<Route component={NotFound}/>
</Switch>
</div>
)
}
}
export default App;
Assuming your are using react-router-dom 4 and above.
Try
import React, { Component } from "react";
import { withRouter } from "react-router-dom"; //<-- import this
import Photo from "./Photo";
class Container extends Component {
componentDidMount() {
// Your this.props.match.id is likely undefined
this.props.getImages(this.props.match.params.id); // <-- Change here
}
...
}
export default withRouter(Container); // <-- Change this
This is code for looping through routes file. Here i am creating routerconfig array an exporting from here to app.js.
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import routes from './routes';
class RouterConfig extends Component {
render() {
return (
routes.map((route) => {
return (
<Route
key={ route.id }
path={ route.path }
exact={ route.exact }
component={ route.component }
/>
);
})
);
}
}
export default RouterConfig;
This is my route configuration file, where i have listed all routes.
import Home from '../components/home/home';
import About from '../components/about/about';
import Posts from '../components/posts/posts';
import NotFound from '../components/not_found/not_found';
const routes = [
{
path: '/',
exact: true,
component: Home,
id: 'home',
},
{
path: '/about',
component: About,
id: 'about',
},
{
path: '/posts',
component: Posts,
id: 'posts',
},
{
component: NotFound,
id: 'not_found',
},
];
export default routes;
This is my app.js
import React, { Component } from 'react';
import { Switch, Router, Route } from 'react-router-dom';
import Header from '../header/header';
import Footer from '../footer/footer';
import history from '../../history';
import RouterConfig from '../../router/browserroute';
class App extends Component {
render() {
return (
<div>
<Router history={ history } >
<div>
<Header />
<Switch>
<RouterConfig />
</Switch>
<Footer />
</div>
</Router>
</div>
);
}
}
export default App;
issue is on every page i am getting not found component render. Even
using switch with routes not getting the expected results. not sure
why code is not working properly.
[this is how it renders not found in every page ][1]
[1]: https://i.stack.imgur.com/B7evW.png
on changing:
class App extends Component {
render() {
return (
<div>
<Router history={ history } >
<div>
<Header />
<Switch>
<Route exact path='/' component={ Home } />
<Route path='/about' component={ About } />
<Route path='/posts' component={ Posts } />
<Route component={ NotFound } />
</Switch>
<Footer />
</div>
</Router>
</div>
);
}
}
This works fine
You need to move Switch to be inside RouteConfig. There must be something with the way React 16 partials work that is unexpected in React Router (I'm not really sure why it does not work when Switch is outside RouterConfig). The good news is, at least for me, it makes sense to be inside RouteConfig.
Here is a codesandbox that works:
https://codesandbox.io/s/8xmx478kq8
Which version of React are you using? If you are using React 15 or lower, it does not support returning an array inside render. You would need to wrap you stuff in a container.
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import routes from './routes';
class RouterConfig extends Component {
render() {
return (
<div>{
routes.map((route) => {
return (
<Route
key={ route.id }
path={ route.path }
exact={ route.exact }
component={ route.component }
/>
);
})
}</div>
);
}
}
export default RouterConfig;
Notice the div wrapping the Routes array
On my attempt to do nested routes, I've failed to have the child components to mount when the route changes through Link or history.push; but if declaring the routes directly in the root.js file, it works. So, ideally I'd like to keep as much routes configuration as possible in the root/routes.js file and not all over the App (I'm iterating over the root/routes.js object instead to do this automatically; I mean... trying)
To break it down logically (it's a bit abstract, but check the code below afterwards please):
- There's a `root/routes.js` that has all the routes configuration (parents, nested components, etc)
- The `root.js` defines the `<Route...>` where the attribute `component` is the return value of a function that passes the `routes` configuration to its `routes` component prop
- the main wrapper iterates over the component prop `routes` and defines `child` routes automatically...I mean...I'm trying...
Why would I want to do this? The way my brain works and why not? Was possible before react router 4
<MyAppWrapper>
<CommonNavigationBar />
<Main>
----- dynamic / changes by route etc -----
</Main>
<Footer />
</MyAppWrapper>
I wonder where my attempt is failing?
// Working version
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
import App from '../app/containers/app'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' component={App} />
</BrowserRouter>
</Provider>
)
}
export default Root
For the previous example, the App component as nested , bellow I'm trying to do that dynamically..and it fails for some reason! It should be exactly the same though...there must be a typoe somewhere...
like,
import React, { Component } from 'react'
import { isBrowser } from 'reactatouille'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { withRouter, Route } from 'react-router'
import Navbar from '../navbar'
import JourneySelector from '../journeySelector'
import reservationFinder from '../../../reservationFinder'
// include the stylesheet entry-point
isBrowser() && require('../../../../sass/app.scss')
class App extends Component {
constructor (props) {
super(props)
this.state = {
init: true
}
}
render () {
return (
<div className={'app' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
<Route exact path='/' component={JourneySelector} />
<Route exact path='/reservation-finder' component={reservationFinder.containers.App} />
</main>
</div>
)
}
}
// export default App
function mapStateToProps (state, ownProps) {
return {
// example: state.example
}
}
function matchDispatchToProps (dispatch) {
return bindActionCreators({
// replay: replay
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(withRouter(App))
While my technique fails (all I'm trying to do is iterate the root/routes children routes to generate these ):
// root/routes.js
import app from '../app'
import reservationFinder from '../reservationFinder'
const rootRoutes = [
{
path: '/',
component: app.containers.App,
exact: true,
routes: [{
path: '/',
exact: true,
component: app.containers.JourneySelector
}, {
path: '/reservation-finder',
component: reservationFinder.containers.App
}]
}
]
export default rootRoutes
The root js file. You see the setRoute fn returns a new component, where the children routes is passed as a props? I believed this would work:
// root.js
import React from 'react'
import { Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} exact={route.exact || false} component={() => (<MyComponent routes={route.routes} />)} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
{ rootRoutes.map(route => setRoute(route)) }
</BrowserRouter>
</Provider>
)
}
export default Root
the main app that I want to use as a wrapper:
// main app
import React, { Component } from 'react'
import { isBrowser } from 'reactatouille'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { withRouter, Route } from 'react-router'
import Navbar from '../navbar'
// include the stylesheet entry-point
isBrowser() && require('../../../../sass/app.scss')
class App extends Component {
constructor (props) {
super(props)
this.state = {
init: true
}
}
render () {
return (
<div className={'app' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
{ Array.isArray(this.props.routes) && this.props.routes.map(route => <Route key={route.path} {...route} />) }
</main>
</div>
)
}
}
// export default App
function mapStateToProps (state, ownProps) {
return {
// example: state.example
}
}
function matchDispatchToProps (dispatch) {
return bindActionCreators({
// replay: replay
}, dispatch)
}
export default connect(mapStateToProps, matchDispatchToProps)(withRouter(App))
I understand I MIGHT be able to achieve something similar, like?!
// root
import React from 'react'
import { Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
import MyAppWrapper from 'xxx/MyAppWrapper'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} exact={route.exact || false} component={() => (<MyComponent routes={route.routes} />)} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<MyAppWrapper>
<Route path='x' component={x} />
<Route path='y' component={y} />
</MyAppWrapper>
</BrowserRouter>
</Provider>
)
}
export default Root
Notes: During testing, I've noticed that it worked server-side? I mean, I may have missed something, and I didn't save my work. Also, when it fails, the previous component (the default) is still mounted and does not unmount
I even tried (without sucess...I wonder if this is a bug):
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import App from '../app/containers/app'
import rootRoutes from './routes'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' render={() => (
<App />
)} />
</BrowserRouter>
</Provider>
)
}
export default Root
Ok, I think this is a bug so reported ( https://github.com/ReactTraining/react-router/issues/5190 ), you can find the live example here ( https://codepen.io/helderoliveira/pen/rmXdgd ), click topic. Maybe what I'm trying to do is not supported, but instead of blank we should get an error message.
Ok, I found the typo. The solution is to use render and pass the routerProps + any other props you desire through Object.assign and the spread operator!
// root.js
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import App from '../app/containers/app'
import rootRoutes from './routes'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<Route path='/' render={routeProps => <App {...Object.assign({}, routeProps, { routes: rootRoutes[0].routes })} />} />
</BrowserRouter>
</Provider>
)
}
export default Root
And the main app wrapper:
class App extends Component {
render () {
return (
<div className={'app kiosk' + ' ' + (!this.state.init && 'uninitialised')}>
<Navbar />
<main>
{ Array.isArray(this.props.routes) && this.props.routes.map(route => <Route key={route.path} exact={route.exact} path={route.path} component={route.component} />) }
</main>
</div>
)
}
}
export default App
the routes file:
import React from 'react'
import { Route } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import rootRoutes from './routes'
const setRoute = (route) => {
const MyComponent = route.component
return <Route key={route.path} path={route.path} render={routeProps => <MyComponent {...Object.assign({}, routeProps, { routes: rootRoutes[0].routes })} />} />
}
const Root = ({store, history}) => {
return (
<Provider store={store}>
<BrowserRouter history={history}>
<div>
{ rootRoutes.map(route => setRoute(route)) }
</div>
</BrowserRouter>
</Provider>
)
}
export default Root
I'm trying to get a very basic app going using Meteor 1.3, React and React-Router. The pages are rendering but am having issues with getting data passed through. I've done some research but unable to find much with this particular mix including use of the container pattern.
All that the app needs to do is surface all items in the 'Thingies' collection on the Test page. Unsure if it's the data publication, routing, container or something else that's incorrect?
The debugging console lines all show 0 in the collection even though the mongo shell definitely shows items in there.
Structure of my project: http://i.stack.imgur.com/WZXFa.png
things.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
export const Thingies = new Mongo.Collection('thingies');
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function thingiesPublication() {
return Thingies.find();
});
}
Test.jsx
import { Meteor } from 'meteor/meteor';
import { Thingies } from '../../api/things.js';
import { createContainer } from 'meteor/react-meteor-data';
import React, { Component } from 'react';
class TestContainer extends Component {
render(){
console.log('Props: ' + this.props.thingies.length);
let RenderThings = this.props.thingies.map(thing => {
return <li key={thing._id}>{thing.text}</li>
});
return (
<div>
<h1>Test this</h1>
<ul>
{ RenderThings }
</ul>
</div>
);
}
}
TestContainer.propType = {
thingies: React.PropTypes.array
};
export default createContainer(() => {
Meteor.subscribe('thingies');
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch(),
};
}, TestContainer);
routes.jsx
import React from 'react';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
// route components
import App from '../../ui/layouts/App.jsx';
import TestContainer from '../../ui/pages/Test.jsx';
import Index from '../../ui/pages/Index.jsx';
export const renderRoutes = () => (
<Router history={browserHistory}>
<Route path="/" component={ App }>
<IndexRoute component={ Index } />
<Route path="index" component={Index}/>
<Route path="test" component={TestContainer}/>
</Route>
</Router>
);
Navigation.jsx
import React from 'react';
import { IndexLink, Link } from 'react-router';
export const Navigation = () => (
<div>
<h4>Navigation</h4>
<ul>
<li><IndexLink to="/" activeClassName="active">Home</IndexLink></li>
<li><Link to="index" activeClassName="active">Index</Link></li>
<li><Link to="test" activeClassName="active">Test</Link></li>
</ul>
</div>
)
App.jsx
import React, { Component } from 'react';
import { Navigation } from '../components/Navigation.jsx';
const App = ( { children } ) => (
<div>
<Navigation />
{ children }
</div>
)
export default App;
Many thanks in advance. I'm sure I'm missing something obvious!
I would try to change the Test.jsx Container code a little bit:
export default createContainer(() => {
if (Meteor.subscribe('thingies').ready()) {
console.log('Container: ' + Thingies.find({}).count());
return {
thingies: Thingies.find({}).fetch()
};
} else {
return {
thingies: null
};
}
}, TestContainer);
EDIT: Try replace your publish method with:
if (Meteor.isServer) {
// This code only runs on the server
Meteor.publish('thingies', function() {
return Thingies.find({});
});
}