I am trying to figure out how the react-router works inside the AppContainer when HMR is enable and stuck upon the following error. Can you please explain what the hack is going on?
Invariant Violation: The root route must render a single element
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
// AppContainer is a necessary wrapper component for HMR
import Routes from './routes/index';
const MOUNT_APP = document.getElementById('root');
const render = () => {
ReactDOM.render(
<AppContainer>
<Routes />
</AppContainer>,
MOUNT_APP
);
};
render();
// Hot Module Replacement API
if (module.hot) {
module.hot.accept('./routes/index', () => {
render()
});
}
The route file is:
import React from 'react';
import { Router, IndexRoute, browserHistory } from 'react-router';
import Home from './Home';
import SubView from './Sub';
const componentRoutes = {
component : Home,
path : '/',
indexRoute : SubView,
childRoutes : [
]
}
const Routes = () => {
return (
<Router history={browserHistory} routes={componentRoutes} />
);
};
export default Routes;
HomeView Component:
import React from 'react';
const HomeView = () => {
<div>
<h4>Welcome</h4>
</div>
}
export default HomeView;
HomeView Route:
import HomeView from './components/SubView';
export default {
component: HomeView
}
P.S: SubView is equal to HomeView.
You need to return one element from the component. Right now your component for HomeView looks like this:
const HomeView = () => {
<div>
<h4>Welcome</h4>
</div>
}
You need to return the markup instead of just put it in the function body like this:
const HomeView = () => {
return (
<div>
<h4>Welcome</h4>
</div>
)
}
Your HomeView component does not return anything. You need to wrap the inner jsx in return ( ... ).
Related
Here is an Error:
Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent}). If you meant to render a collection of children, use an array instead.
It works just fine if I don't use connect in main.js and using connect in App.js doesn't make an Error, but once I use connect in main.js it throws me this error. What do I do wrong? And I'm using connect same way as in App.js Thank you
Here is sandBox https://codesandbox.io/s/busy-euler-7mpi7?file=/src/main.js
you can experience, just delete connect in main.js and it will start working
App.js
import React, { useEffect } from "react";
import './styles/main.scss';
import './App.scss';
import routes from "./router/router";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
import {checkUser, fetchUsers, fetchPolls} from "./store/index";
function App (props) {
useEffect(() => {
let { loadUsers} = props
loadUsers();
}, [])
let jsxRoutes = routes.map(el =>
<Route
path={el.url}
exact={el.exact}
key={el.url}>
{ el.component }
</Route>
)
return (
<Router>
<div className="App">
<Switch>
{ jsxRoutes }
</Switch>
</div>
</Router>
);
}
const mapStateToProps = state => {
return {
users: state.users.data,
}
}
const mapDispatchToProps = dispatch => {
return {
loadUsers: () => dispatch(fetchUsers())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Main.js
import React from "react";
import "./main.scss"
import { connect } from "react-redux"
const Main = (props) => {
return(
<main>
main pg
</main>
)
}
export default connect(null, null)(Main);
In App.js try this instead:
let jsxRoutes = routes.map((el) => (
<Route path={el.url} exact={el.exact} key={el.url} component={el.component} />
));
Or the shorter version: <Route {...el} />
I expect that console.log('Refresh') runs every time the route changes (switching from Component1 to Component2). But it's only triggering on first render. Why?
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
App.js:
import React, { useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';
import Nav from './Nav';
import Component1 from './Component1';
import Component2 from './Component2';
const App = () => {
useEffect( () => console.log('Refresh'));
return (
[<Switch>
<Route component = {Nav}/>
</Switch>,
<Switch>
<Route exact path = '/component1' component = {Component1}/>
<Route exact path = '/component2' component = {Component2}/>
</Switch>]
);
}
export default App;
Nav.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Nav = () => {
return (
<div>
<Link to = '/component1'>Component 1</Link>
<Link to = '/component2'>Component 2</Link>
</div>
);
}
export default Nav;
Component1.js:
import React from 'react';
const Component1 = () => {
return (
<div>
<p>Hi</p>
</div>
);
}
export default Component1;
Component2.js:
import React from 'react';
const Component2 = () => {
return (
<div>
<p>Bye</p>
</div>
);
}
export default Component2;
The useEffect is not triggered because the App component is not re-rendered, nothing changed in that component (no state or props update).
If you want the App component to re-render when the route change, you can use the withRouter HOC to inject route props, like this :
import { Switch, Route, withRouter } from 'react-router-dom';
const App = () => {
useEffect( () => console.log('Refresh'));
return (...);
}
export default withRouter(App);
Example : https://codesandbox.io/s/youthful-pare-n8p1y
use the key attribute so everytime we render new component (different key)
<Route path='/mypath/:username' exact render= {routeProps =><MyCompo {...routeProps} key={document.location.href} />} />
Use the 2nd argument to useEffect to conditionally apply effect. For example via react-router-dom, you get some properties
const { schoolId, classId } = props
useEffect(() => {
// fetch something here
}, [schoolId, classId)
Here [schoolId, classId acts as the unique identifier for useEffect to trigger.
Using Hooks:
use useLocation and useLayoutEffect get more efficiency:
import { useLocation } from "react-router-dom";
//...
const location = useLocation();
//...
useLayoutEffect(() => {
console.log("location",location)
}, [location])
I'm using react-router v4.2.2 in my project, and am trying to create a set of cards that each link to other components. Right now I'm just testing that the router works, by routing each Card to one specific component called 'Project1'. This, however, is not working; I'm not seeing the div inside the Project1 component pop up. What am I doing wrong?? Shouldn't each Card link to the Project1 component?
Here is the code for the main container that holds the cards:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import ProjectCard from '../components/project_card.js';
import Project1 from '../components/project1.js';
class ProjectCards extends React.Component {
render() {
var projectCards = this.props.projects.map((project, i) => {
return (
<div key={i}>
<Link to={`/${project.title}`}>
<ProjectCard title={project.title} date={project.date} focus={project.focus}/>
</Link>
</div>
);
});
return (
<div>{projectCards}</div>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(ProjectCards);
Here is the code for the Routes container:
import React from 'react';
import Project1 from '../components/project1.js';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
class Routes extends React.Component{
render() {
var createRoutes = this.props.projects.map((project, i) => {
return <Route key={i} exact path={`/${project.title}`} component={Project1}/>
});
return (
<Switch>
{createRoutes}
</Switch>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(Routes);
Here is the code for the index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import ReduxPromise from 'redux-promise';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App.jsx';
import css from '../style/style.css';
import style from '../style/style.css';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
and the code for Project1, which should display when a Card has been clicked:
import React from 'react';
const Project1 = () => {
return (
<div>hello there this is Project1</div>
);
}
export default Project1;
When you click on a link, you navigate to Project1, which has no Routes defined. You basically destroy your Route when you lick on it because the Switch is in the same component as the Link. The Switch statement needs to be moved to a 3rd component so that it still exists after clicking on a linking card.
Okay, I've spent three hours driving myself crazy trying to figure this out. I'm sure I'm doing something wrong, but this is my first foray into React and I'm not sure exactly what the problem is.
My main app file looks like this:
import React, { PropTypes } from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import createHistory from 'history/createBrowserHistory'
import routes from './routes.jsx';
import map from 'lodash/map';
import MainLayout from './components/layouts/main-layout.jsx';
const history = createHistory();
const App = ({store}) => (
<Provider store={store}>
<Router history={history} basename="/admin-panel">
<Route component={MainLayout}>
<Switch>
{map(routes, (route, name) => (
<Route key={name} path={route.path} exact={route.exact} component={route.component} />
))}
</Switch>
</Route>
</Router>
</Provider>
);
App.propTypes = {
store: PropTypes.object.isRequired,
};
export default App;
The route is simply a JSON object that contains route info. Example:
import React from 'react';
import DashboardContainer from './components/containers/dashboard-container.jsx';
import AdminUserContainer from './components/containers/admin-users-container.jsx';
export default {
dashboard: {
order: 1,
path: '/',
exact: true,
name: 'Dashboard',
icon: 'tachometer',
component: DashboardContainer,
},
users: {
order: 2,
path: '/admin-users',
name: 'Admin Users',
icon: 'users',
component: AdminUserContainer,
}
};
(There's irrelevant stuff in the object; that's for the sidebar rendering, which also uses this object to render itself)
dashboard-component.jsx looks like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Dashboard from '../views/dashboard.jsx';
class DashboardContainer extends Component {
render () {
return (<Dashboard />);
}
}
const mapStateToProps = function (store) {
return store;
};
export default connect(mapStateToProps)(DashboardContainer);
And dashboard.jsx looks like this:
import React, { Component } from 'react';
class Dashboard extends Component {
render () {
return (
<p>This is a test.</p>
);
}
}
export default Dashboard;
But for some reason, no matter what, the returned component is UnknownComponent. Apparently, nothing is matching on /admin-panel, and I'm not sure why. I've tried moving components around, merging the Routing stuff with MainLayout (which just contains the stuff that won't change every request, like the sidebar and the header), even tried the HashRouter instead of the BrowserRouter to see if that would do anything. No dice.
Can someone familiar with React tell me just what it is I'm not doing right here?
So it's my first time setting something like this up and I'm struggling a little bit.
I used https://github.com/reactjs/react-router/tree/master/examples/huge-apps as my source of learning and am trying to set up a very basic layout for a react application.
What seems to be happening is that react isn't re-rendering after a path change therefore nothing ever gets added to the dom
When I click on the go to home component Link the URL bar changes but no DOM changes occur...
Here is my code [i'm leaving out my directory structure since i don't think it's important for the problem]
index.jsx: Load up the react app and get all routes
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Router, browserHistory } from 'react-router';
import Routes from './app/Routes.js';
render(
<Router
history={browserHistory}
routes={Routes}
/>,
document.querySelector('.js-mount-point')
);
Routes.js: Constant to keep all of my routes so that I don't have to manually specify them in index.js
import App from './App.jsx';
import Home from '../routes/Home/Home.js';
const Routes = {
path: '/',
component: App,
childRoutes: [
Home,
],
};
export default Routes;
App.jsx: Parent most component for my app
import React from 'react';
import { Link } from 'react-router';
const App = props => {
console.log(props);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="#/home">Go to Home Component</Link></p>
{props.children}
</div>
);
};
export default App;
Home.js Grab all my route information and getComponent lives here
const HomeRoute = {
path: 'home',
title: 'Home',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./HomeComponent.jsx').default);
});
},
};
export default HomeRoute;
HomeComponent.jsx my very basic home component
import React from 'react';
const HomeComponent = () => (
<div>
<h2>Welcome Home</h2>
</div>
);
export default HomeComponent;
Edit1: Made App.jsx pure function
Edit2: Fixed Routes.js
Edit3: Fixed Home.js
Edit4: Final fix,
const HomeComponent =
changed to
const HomeComponent = () => (
You App should be a component not a function returning an object. Right now you are mixing two approaches.
Either a function (stateless component)
import React from 'react';
import { Link } from 'react-router';
const App = props => {
console.log(props.children);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="/home">Go to Home Component</Link></p>
{props.children}
</div>
);
};
export default App;
Or a statefull component that has render method
import React, { Component } from 'react';
import { Link } from 'react-router';
class App extends Component {
render() {
console.log(this.props.children);
return (
<div>
<h1>Hello World!</h1>
<p><Link to="/home">Go to Home Component</Link></p>
{this.props.children}
</div>
);
}
};
export default App;
Same with HomeComponent
const HomeComponent = () => (
<div>
<h2>Welcome Home</h2>
</div>
);
And you need to fix route config as well
const HomeRoute = {
path: 'home', //no # needed
title: 'Home',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('./HomeComponent.jsx').default);
});
},
};
Ohh, and I think you need.
const Routes = {
path: '/',
component: App,
childRoutes: [
Home
],
};