Unfortunately, I can't get React Router to work in my custom meteor boilerplate and I really can't figure out why. Here's all the files that could potentially be relevant to the problem:
\client\main.js:
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from '../imports/startup/client/routes.jsx';
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('app'));
});
\imports\startup\client\routes.jsx:
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
// route components
import App from '../../ui/App.jsx';
export const renderRoutes = () => (
<Router>
<div>
<Route path="/" component={App} />
</div>
</Router>
);
\imports\ui\App.jsx
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h1>Hey!</h1>
);
}
}
export default withTracker(() => {
return {
};
})(App);
Any idea why the error message might occur? Thanks!
Not sure what version of meteor and react you are using but tha's how i did it on the last project i had.
Try this changes:
import {Router, Route, browserHistory} from 'react-router';
export const renderRoutes = () => (
<Router history={browserHistory}>
<div>
<Route path="/" component={App} />
</div>
</Router>
);
Related
I have an app which just loads a blank page on start up, yet refreshing the page causes it to load correctly.
This is what I'm trying:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './Assets/css/app.css';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
window.processIntent = intent => {
...cordova stuff
};
window.setupAltIntent = () => {
...cordova stuff
};
function startApp() {
window.intentUrlToPrint = '';
window.setupAltIntent();
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
}
if (!window.cordova) {
startApp();
} else {
document.addEventListener('deviceready', startApp, false);
}
App.js
import React, { Component } from 'react';
import AppActivity from './Views/activity/AppActivity';
import Header from './Components/header';
import AppRouter from './AppRouter';
class App extends Component {
componentDidMount() {
if (navigator.splashscreen) {
navigator.splashscreen.hide();
}
}
render() {
return (
<div>
<div>
<Header />
<AppRouter />
</div>
<AppActivity />
</div>
);
}
}
export default App;
AppRouter.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './Pages/Home';
import Contact from './Pages/Contact';
import LogInForm from './Pages/RegisterAndLogin/LogIn';
const AppRouter = () => (
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/contact" component={Contact} />
<Route path="/login" component={LogInForm} />
</Switch>
</div>
);
export default AppRouter;
Header.js
import React, { Component } from 'react';
import HeaderNotLoggedIn from './Headers/HeaderNotLoggedIn';
import HeaderLoggedIn from './Headers/HeaderLoggedIn';
class Header extends Component {
constructor(props) {
super(props);
this.isLoggedOn = this.isLoggedOn.bind(this);
}
isLoggedOn() {
return localStorage.getItem('user');
}
render() {
const header = this.isLoggedOn() ? <HeaderLoggedIn /> : <HeaderNotLoggedIn />;
return <div>{header}</div>;
}
}
export default Header;
Quite new to react, is there anything I'm missing here?
Why does the app only load correctly on page refresh?
I don't have a way to to solve your exact problem, because there are a lot of components, logic, and connections I don't have access to, and so I have to make guesses about. I assume that setupAltIntent returns a promise (though it doesn't totally matter if it does something else). In App I set it up to conditionally render Header once the data has been loaded into the state, which happens when setState is called after the promise is resolved.
Hopefully this gets you far enough to figure out what exactly you need for your own solution.
Link to my solution:
Hi I am new in react and I want to implement routing with Loadable, But Its not working Its showing blank page when either http://localhost:3000/user or http://localhost:3000/
Could you please correct me where I am doing wrong.
I am also getting-
Warning: Failed prop type: Invalid prop component of type string supplied to Route, expected function.
My codes are:
home.js
import React, { Component } from 'react';
import { Link} from 'react-router-dom';
class Home extends Component {
render() {
return (
<div>
<h1>Welcome to the Tornadoes Website!</h1>
<h5><Link to="/user">User</Link></h5>
</div>
);
}
}
export default Home;
user.js:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class User extends Component {
render() {
return (
<div>
<ul>
<li>6/5 # Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 # United</li>
<li><Link to="/">Home</Link></li>
</ul>
</div>
)
}
}
export default User;
App.js:
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import { history } from './helpers/history';
import Loadable from 'react-loadable';
import './App.css';
const Loading = () => <div> Loading... </div>;
const Home = Loadable({
loader: () => import('./components/home-component/home'),
loading: Loading
});
const User = Loadable({
loader: () => import('./components/user-component/user'),
loading: Loading
});
class App extends React.Component {
render() {
return (
<Router history={history}>
<Switch>
<Route exact path="/" component="Home" />
<Route path="/user" component="User" />
</Switch>
</Router>
);
}
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { BrowserRouter } from 'react-router-dom'
ReactDOM.render((
<BrowserRouter>
<App />
</BrowserRouter>
), document.getElementById('root'))
registerServiceWorker();
I see you are doing this: <Route exact path="/" component="Home" /> which should be <Route exact path="/" component={Home} /> since you want to use that variable, it's impossible to reference by String when he can't know which Component you want. I hope this helps
This looks to me like there is a isRequired propType that you have missed when calling your component. Can you post your components here as well?
I want to know if the user is leaving the page
I tried :
componentWillMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
},
But I get :
this.props.router is undefined
I saw some solutions, but I don't want to use this react way :
React.createClass
Version of react-router :
"react-dom": "^16.3.1",
"react-router-dom": "^4.2.2",
And how I implemented the router :
root.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from '../components/app'
import { BrowserRouter } from 'react-router-dom'
// Render component with data
document.addEventListener('DOMContentLoaded', () => {
const node = document.getElementById('app');
ReactDOM.render(
(<BrowserRouter>
<App />
</BrowserRouter>),
node )
});
app.jsx
import React from 'react'
import CompanyList from './company/company_list'
import CompanyDetails from './company/company_details'
import { CompanyNew } from "./company/company_new";
import { Error404 } from "./common/error_404";
import { Route, Switch } from 'react-router-dom'
export const App = () => {
return (
<Switch>
<Route path='/' exact component={CompanyList}/>
<Route path='/companies' component={CompanyList}/>
<Route path='/company/new' component={CompanyNew}/>
<Route path='/company/:id' component={CompanyDetails}/>
/* 404 if not found : */
<Route component={Error404}/>
</Switch>
)
};
EDIT :
I tried with :
import { withRouter } from 'react-router'
class CompanyDetails extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
}
}
export default withRouter(CompanyDetails)
But I get the same error
You can get history off context or create it outside the Router and pass it in as a history prop.
When calling history.push('/packages') the url is updated but the component will not mount (render) unless the page is reloaded. If I call createHistory({forceRefresh: true}) or manually reload the page the UI is rendered correctly. How can I configure react-router-dom to load the component without explicitly reloading the page or using forceRefresh?
index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import store from './store'
import {Provider} from 'react-redux'
import App from './App'
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('app')
);
App.jsx
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom'
import PropTypes from 'prop-types'
import { Navbar, PageHeader, Grid, Row, Col } from 'react-bootstrap'
import LoginFormContainer from './components/Login/LoginFormContainer'
import PackageContainer from './components/Package/PackageContainer'
class App extends Component {
render() {
return (
<Grid>
<Navbar>
<Navbar.Header>
<Navbar.Brand><h3>Mythos</h3></Navbar.Brand>
</Navbar.Header>
</Navbar>
<Row className="content">
<Col xs={12} md={12}>
<Switch>
<Route path='/packages' render={() => <PackageContainer />} />
<Route exact path='/' render={() => <LoginFormContainer />} />
</Switch>
</Col>
</Row>
</Grid>
)
}
}
export default App
loginActions.jsx
import * as types from './actionTypes'
import LoginApi from '../api/loginApi'
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
export function loginUser(user) {
return function(dispatch) {
return LoginApi.login(user).then(creds => {
dispatch(loginUserSuccess(creds));
}).catch(error => {
throw(error);
});
}
}
export function loginUserSuccess(creds) {
sessionStorage.setItem('credentials', JSON.stringify(creds.data))
history.push('/packages')
return {
type: types.LOGIN_USER_SUCCESS,
state: creds.data
}
}
PackageContainer.jsx
import React, { Component } from 'react'
import {connect} from 'react-redux'
import PropTypes from 'prop-types'
import {loadPackages} from '../../actions/packageActions'
import PackageList from './PackageList'
import ImmutablePropTypes from 'react-immutable-proptypes'
import {Map,fromJS,List} from 'immutable'
import {withRouter} from 'react-router-dom'
class PackageContainer extends Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this.props.dispatch(loadPackages());
}
render() {
return (
<div className="col-lg-12">
{this.props.results ?
<PackageList results={this.props.results} /> :
<h3>No Packages Available</h3>}
</div>
);
}
}
PackageContainer.propTypes = {
results: ImmutablePropTypes.list.isRequired,
};
const mapStateToProps = (state, ownProps) => {
return {
results: !state.getIn(['packages','packages','results']) ? List() : state.getIn(['packages','packages','results'])
};
}
PackageContainer = withRouter(connect(mapStateToProps)(PackageContainer))
export default PackageContainer
I assume, that issue that you are create new instance of history object but BrowserRouter doesn't know about the changes which happens inside of it.
So, you should create history object and export it in the index.jsx and use Router instead of BrowserRouter and pass as history property, so then you can just import it whenever you need.
For example:
index.jsx
import { Router } from 'react-router-dom'
import createHistory from 'history/createBrowserHistory'
...
export const history = createHistory()
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<App />
</Router>
</Provider>,
document.getElementById('app')
);
Then, in loginActions you just import history and use .push method as before.
loginActions.jsx
import * as types from './actionTypes'
import LoginApi from '../api/loginApi'
import { history } from './index'
export function loginUser(user) {
return function(dispatch) {
return LoginApi.login(user).then(creds => {
dispatch(loginUserSuccess(creds));
}).catch(error => {
throw(error);
});
}
}
export function loginUserSuccess(creds) {
sessionStorage.setItem('credentials', JSON.stringify(creds.data))
history.push('/packages')
return {
type: types.LOGIN_USER_SUCCESS,
state: creds.data
}
}
Hope it will helps.
In the App.jsx
<Switch>
<Route path='/packages' render={(props) => <PackageContainer {...props}/>} />
<Route exact path='/' render={(props) => <LoginFormContainer {...props}/>} />
</Switch>
Now both PackageContainer and LoginFormContainer have access to history object
I am trying to render a specific component inside of another component based on React, React-Router v4, and Redux in my main 'panel' wrapped in a fixed header and sidebar component.
For example when I select an item from the sidebar, I to render the Detail panel and and load the details based on the id, like: <Route path='/item/:id' component={ItemDetail} />
routes.js
import React, { Component } from 'react';
import { RouteHandler, Switch, Route, DefaultRoute } from 'react-router';
import App from './containers/App';
import Login from './containers/Login';
import LobbyDetail from './components/LobbyDetail';
export default (
<Switch>
<Route exact path="/" component={App} />
<Route exact path="/login" component={Login} />
</Switch>
);
app.js:
import React, { Component } from 'react'
import { Router, Route, Link } from 'react-router'
import { connect } from 'react-redux'
import PropTypes from 'prop-types';
import auth from '../actions/auth';
import Sidebar from '../Components/Sidebar'
class App extends Component {
static propTypes = {
};
/**
*
*/
render() {
const { ... } = this.props
return (
<div className="container-fluid">
<div className="row">
{* I WANT TO RENDER DYNAMIC COMPONENT HERE *}
</div>
<Sidebar currentUser={currentUser}
logout={logout}
/>
</div>
);
}
}
// ...
export default connect(mapStateToProps, mapDispatchToProps)(App)
index.js (basically main app):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createMemoryHistory } from 'history';
import routes from './routes';
import configureStore from './store/store.js';
import { AppContainer } from 'react-hot-loader';
const syncHistoryWithStore = (store, history) => {
const { routing } = store.getState();
if (routing && routing.location) {
history.replace(routing.location);
}
};
const initialState = {};
const routerHistory = createMemoryHistory();
const store = configureStore(initialState, routerHistory);
syncHistoryWithStore(store, routerHistory);
const rootElement = document.querySelector(document.currentScript.getAttribute('data-container'));
const render = () => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<ConnectedRouter history={routerHistory}>
{routes}
</ConnectedRouter>
</Provider>
</AppContainer>,
rootElement
);
}
render();
if (module.hot) { module.hot.accept(render); }
What you're looking for is parameterized routing. Make a <Route/> like the following: <Route path='/item/:id' component={ MyComponent } />.
Now in MyComponent you can use the value of props.match.params.id to conditionally render, or if you're trying to load async data based on the value of :id; You can use the componentWillReceiveProps life cycle method and dispatch an action based on the value of this.props.match.params.id.
Note: <Link to='/item/some-item'/> will set the value of match.params.id to 'some-item'.