Entering animations are applied correctly. Component seems to unmount prior to applying any leave, leave-active classes.
componentWillMount() {
this.setState({
routes: [(<Route exact path='/' component={HomeView}/>),
(<Route exact path='/account' component={YourAccountView}/>),
(<Route exact path='/settings' component={SettingsView}/>),
(<Route exact path='/about' component={AboutView}/>),
(<Route exact path='/machine/:_id' component={MachineDetailView}/>),
(<Route exact path='/floorview' component={FloorView}/>)]
})
}
render() {
return (
<div>
<NavBar/>
<div style={{position: 'relative', flexGrow: 1 , marginTop:40+'px'}}>
<ReactCSSTransitionGroup
transitionName="pageSlider"
transitionEnter={true}
transitionLeave={true}
transitionEnterTimeout={500}
transitionLeaveTimeout={500}>
{this.state.routes
.filter((e)=> e.props.path===this.context.router.history.location.pathname )
.map((e)=> React.cloneElement(e, { key: this.context.router.history.location.pathname} ))}
</ReactCSSTransitionGroup>
</div>
</div>
);
}
I can't tell if this is a ReactCSSTransitionGroup thing, or a React-Router v4 mounting/unmounting thing. Has anyone run into and solved similar issue?
this case works:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Route} from 'react-router-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<BrowserRouter>
<Route path="/" component={App}/>
</BrowserRouter>
,
document.getElementById('root')
);
App.js
import React, { Component } from 'react';
import { Switch, Route, Link } from 'react-router-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './App.css';
const One = ({match}) => (
<h2>{match.url}</h2>
)
const Two = ({match}) => (
<h2>{match.url}</h2>
)
const Three = ({match}) => (
<h2>{match.url}</h2>
)
const Four = ({match}) => (
<h2>{match.url}</h2>
)
const MyNav = () => (
<div>
<Link to='/One'>One</Link>
<Link to='/Two'>Two</Link>
<Link to='/Three'>Three</Link>
<Link to='/Four'>Four</Link>
</div>
)
class App extends Component {
componentWillMount() {
this.setState({routeKey:this.props.location.pathname})
this.setState({routes: [
(<Route exact path="/One" component={One}/>),
(<Route exact path="/Two" component={Two}/>),
(<Route exact path="/Three" component={Three}/>),
(<Route exact path="/Four" component={Four}/>)
]})
}
render() {
return (
<div className="App">
<div>
<MyNav/>
<ReactCSSTransitionGroup
transitionName="PageSlider"
transitionEnterTimeout={0}
transitionLeaveTimeout={150}>
<Switch key={this.props.location.pathname}>
<Route exact path="/One" component={One}/>
<Route exact path="/Two" component={Two}/>
<Route exact path="/Three" component={Three}/>
<Route exact path="/Four" component={Four}/>
</Switch>
</ReactCSSTransitionGroup>
</div>
</div>
);
}
componentWillReceiveProps(newProps) {
this.setState({routeKey:newProps.location.pathname})
}
}
export default App;
specify .PageSlider-enter, .PageSlider-enter.PageSlider-enter-active, .PageSlider-leave, .PageSlider-leave.PageSlider-leave-active accordingly
Related
I am developing a dropdown Sidebar with ReactJS, and using separate files with the name of sizeConfig and colorConfigs and access it in mainLayout page using material UI.
sizeConfigs.js:
const sizeConfigs = {
sidebar:{
width:"300px"
}
}
export default sizeConfigs
colorConfigs.js:
import { colors } from "#mui/material";
const colorConfigs = {
sidebar: {
bg: "#233044",
color: "#eeeeee",
hoverBg: "#1e293a",
activeBg: "#1e253a"
},
topbar: {
bg: "#fff",
color: "#000"
},
mainBg: colors.grey["100"]
};
export default colorConfigs;
MainLayout.jsx:
import { Box, Toolbar } from '#mui/material'
import React from 'react'
import Topbar from '../common/Topbar'
import sizeConfigs from '../../configs/sizeConfigs'
import colorConfigs from '../../configs/colorConfigs'
import Sidebar from '../common/Sidebar'
import { Outlet } from 'react-router-dom'
const MainLayout = () => {
return (
<Box sx={{display:"flex"}}>
<Topbar />
{/*2nd Box*/}
<Box componenet="nav" sx={{
width:sizeConfigs.sidebar.width,
flexShrink:0
}}>
<Sidebar />
</Box>
{/*3rd Box*/}
<Box component="main" sx={{
flexGrow:1,
p:3,
width:`calc(100%-${sizeConfigs.sidebar.width})`,
minHeight: "100vh",
backgroundColor:colorConfigs.mainBg
}}>
<Toolbar />
<Outlet />
</Box>
</Box>
);
};
export default MainLayout
App.jsx File:
import {BrowserRouter,Routes,Route} from 'react-router-dom'
import MainLayout from './components/layout/MainLayout';
//import HomePage from './pages/home/HomePage';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<MainLayout/>}/>
</Routes>
</BrowserRouter>
);
}
export default App;
This code is showing my sidebar with main page, but when I add 2nd Route with index element, only homepage is showing on my main screen sidebar is not showing.
Code after adding 2nd Route:
import {BrowserRouter,Routes,Route} from 'react-router-dom'
import MainLayout from './components/layout/MainLayout';
import HomePage from './pages/home/HomePage';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<MainLayout/>}/>
<Route index element={<HomePage/>}/>
</Routes>
</BrowserRouter>
);
}
export default App;
Please help me finding the mistake why my code is not displaying the sidebar and home page with display:flex as shown in the image:
Goal:
Display the content of the code in relation to
<Router>
{isAuthenticated ? authenticatedRoutes : nonAuthenticatedRoutes}
</Router>
Problem:
When I use codesandbox or Stackblitz it wont display the content. However, when I use my local computer with VS code.
Same code that is used in my local development is the same for the sandbox and stackblitz.
In the end the result display in local development but not in codesandbox stand stackblitz.
However, it creates a lot of message and error about
"Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render."
Question:
How should it be solved in order to be displayed at stackblitz and also not displaying many message.
Info:
*The foundation of the code is from this webpage (https://stackblitz.com/edit/react-ts-conditional-route-e9gscp)
*If you dont want to show the error message, exchange the code to
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
Stackblitz:
https://stackblitz.com/edit/react-ts-xjaspz
Sandbox:
https://codesandbox.io/s/confident-swanson-uie1n1
Thank you!
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.tsx
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Link, Route, Routes, Navigate } from 'react-router-dom';
import Login from './Login';
import Home from './Home';
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
useEffect(() => {
}, [isAuthenticated]);
const handle_login = () => {
setIsAuthenticated(true);
};
const handle_logout = () => {
setIsAuthenticated(false);
};
const authenticatedRoutes = (
<React.Fragment>
<Routes>
<Route
path="/home"
element={<Home handle_logout={handle_logout} />}
/>
</Routes>
<Navigate to="/home" />
</React.Fragment>
);
const nonAuthenticatedRoutes = (
<React.Fragment>
<Routes>
<Route
path="/login"
element={<Login handle_login={handle_login} />}
/>
</Routes>
<Navigate to="/login" />
<br />
<br />
</React.Fragment>
);
return (
<Router>
{isAuthenticated ? authenticatedRoutes : nonAuthenticatedRoutes}
</Router>
);
};
export default App;
*
{isAuthenticated ? authenticatedRoutes : nonAuthenticatedRoutes}
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
*/
login.tsx
import React from "react";
interface LoginProps {
handle_login: () => void;
}
const Login: React.FC<LoginProps> = props => {
return (
<React.Fragment>
<title> Login </title>
<div className="container">
<strong>login page</strong> <br />
<br />
<button onClick={props.handle_login}> Login </button>
</div>
</React.Fragment>
);
};
export default Login;
Home.tsx
import React from 'react';
interface HomeProps {
handle_logout: () => void;
}
const Home: React.FC<HomeProps> = (props) => {
return (
<React.Fragment>
<header>
{/* <h2>Home </h2> */}
<button slot="end" onClick={props.handle_logout}>
{' '}
Logout{' '}
</button>
</header>
<title> Home </title>
<div className="container">
<strong>Home page</strong>
<p>Click logout on the titlebar to logout </p>
</div>
</React.Fragment>
);
};
export default Home;
https://stackblitz.com/edit/react-ts-eghbyc?file=index.tsx
App.tsx
import React, { useEffect, useState } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import Home from './Home';
import Login from './Login';
const App: React.FC = () => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
useEffect(() => {
console.log('Authentication state - ', isAuthenticated);
}, [isAuthenticated]);
const handle_login = () => {
setIsAuthenticated(true);
};
const handle_logout = () => {
setIsAuthenticated(false);
};
const authenticatedRoutes = (
<React.Fragment>
<Routes>
<Route
path="/home"
element={<Home handle_logout={handle_logout} />}
/>
<Route
path="*"
element={<Navigate to="/home" />}
/>
</ Routes>
</React.Fragment>
);
const nonAuthenticatedRoutes = (
<React.Fragment>
<Routes>
<Route
path="/login"
element={<Login handle_login={handle_login} />}
/>
<Route
path="*"
element={<Navigate to="/login" />}
/>
</ Routes>
<br />
<br />
<br />
<br />
</React.Fragment>
);
return (
<Router>
{/* <IonRouterOutlet> */}
{isAuthenticated ? authenticatedRoutes : nonAuthenticatedRoutes}
{/* </IonRouterOutlet> */}
</Router>
);
};
export default App;
Home.tsx
import React from 'react';
interface HomeProps {
handle_logout: () => void;
}
const Home: React.FC = (props) => {
return (
<React.Fragment>
{/* Home */}
<button slot="end" onClick={props.handle_logout}>
{' '}
Logout{' '}
</button>
</header>
<title> Home </title>
<div className="container">
<strong>Home page</strong>
<p>Click logout on the titlebar to logout </p>
</div>
</React.Fragment>
);
};
export default Home;
index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Login.tsx
import React from "react";
interface LoginProps {
handle_login: () => void;
}
const Login: React.FC<LoginProps> = props => {
return (
<React.Fragment>
<title> Login </title>
<div className="container">
<strong>login page</strong> <br />
<br />
<button onClick={props.handle_login}> Login </button>
</div>
</React.Fragment>
);
};
export default Login;
I made an simple taskshceduler with react router 5 and react redux and work fine still.
Now I try animating between different route with react-transition-group.
If I click the url changed BUT the screen not re-render until i reload the page mannualy.
Th animation only works when i use the backward button on the browser
I not get error to the console.
What is the problem and What is the solution?
Thank you
App.js
import { Fragment, useEffect, Suspense } from "react";
import { useSelector, useDispatch } from "react-redux";
import Notification from "./shared/UIElements/Notification";
import { sendCartData, fetchCartData } from "./store/cart-actions";
import Auth from "./Auth/page/Auth";
import TaskMain from "./tasks/page/TaskMain";
import TaskFilter from "./tasks/page/TaskFilter";
import MainNavigation from "./Layout/Navigation/MainNavigation";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import "../src/scss/styles.css";
import UpdateTask from "./tasks/page/UpdateTask";
import {
BrowserRouter as Router,
Route,
Redirect,
Switch,
useLocation,
} from "react-router-dom";
import NewTask from "./tasks/page/NewTask";
let isInitial = true;
function App() {
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
const logged = useSelector((state) => state.cart.logged);
const location = useLocation();
useEffect(() => {
dispatch(fetchCartData());
console.log(`fetch usefeect`);
}, [dispatch]);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
if (cart.changed) {
dispatch(sendCartData(cart));
}
}, [cart, dispatch]);
let routes;
if (!logged) {
routes = (
<Switch location={location}>
<Route path="/" exact>
<Auth />
</Route>
<Redirect to="/" />
</Switch>
);
} else {
routes = (
<Switch location={location}>
<Route path="/" exact>
<TaskMain />
</Route>
<Route path="/tasks/new" exact>
<NewTask />
</Route>
<Route path="/tasks/update/:id" exact>
<UpdateTask />
</Route>
<Route path="/items" exact>
<TaskFilter />
</Route>
<Redirect to="/" />
</Switch>
);
}
return (
<Router>
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainNavigation />
<TransitionGroup>
<CSSTransition
timeout={1250}
classNames='fade'
key={location.key}
>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
{routes}
</Suspense>
</CSSTransition>
</TransitionGroup>
</Fragment>
</Router>
);
}
export default App;
App.css
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
transition: opacity 1250ms ease-in;
opacity: 0;
}
MainNavigation.js
import React, { useState } from 'react';
import NavLinks from './NavLinks';
import SideDrawer from './SideDrawer';
import Backdrop from '../UIElements/Backdrop';
const MainNavigation = (props) => {
const [drawerIsOpen, setDrawerIsOpen] = useState(false);
const openDrawerHandler = () => {
setDrawerIsOpen(true);
};
const closeDrawerHandler = () => {
setDrawerIsOpen(false);
};
return (
<React.Fragment>
{drawerIsOpen && <Backdrop onClick={closeDrawerHandler} />}
<SideDrawer show={drawerIsOpen} onClick={closeDrawerHandler}>
<nav className="main-navigation__drawer-nav">
<NavLinks />
</nav>
</SideDrawer>
<header className="main-header header-grid">
<button
className="main-navigation__menu-btn"
onClick={openDrawerHandler}
>
<span />
<span />
<span />
</button>
<div className="title-grid">
<h1 className="main-navigation__title fontsize-18">TODO APP</h1>
</div>
<div className="links-grid">
<NavLinks />
</div>
</header>
</React.Fragment>
);
};
export default MainNavigation;
NavLink.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import {useHistory } from "react-router-dom";
import { cartActions } from "../../store/cart-slice";
const NavLinks = (props) => {
const logged = useSelector((state) => state.cart.logged);
const history = useHistory();
const dispatch = useDispatch();
const foOldalHandler = () => {
history.push(`/`);
};
const szuresHandler = () => {
history.push(`/items`);
};
const logoutHanler = () => {
dispatch(cartActions.logout());
};
return (
<React.Fragment>
<div>
<nav className="nav-links">
<button className="nav-links-button" onClick={foOldalHandler}>Főoldal</button>
{logged && <button className="nav-links-button" onClick={szuresHandler}>Szűrés</button>}
{logged && <button className="nav-links-button" onClick={logoutHanler}>Kijelentkezés</button>}
</nav>
</div>
</React.Fragment>
);
};
export default NavLinks;
SideDrawer.js
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
const SideDrawer = props => {
const content = (
<CSSTransition
in={props.show}
timeout={200}
classNames="slide-in-left"
mountOnEnter
unmountOnExit
>
<aside className="side-drawer" onClick={props.onClick}>{props.children}</aside>
</CSSTransition>
);
return ReactDOM.createPortal(content, document.getElementById('drawer-hook'));
};
export default SideDrawer;
index.js
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/index';
import './index.css';
import App from './App';
import {
BrowserRouter,
BrowserRouter as Router,
} from "react-router-dom";
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
NewTask.js
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { cartActions } from "../../store/cart-slice";
import { v4 as uuid } from "uuid";
export default function NewTask() {
const dispatch = useDispatch();
const username = useSelector((state) => state.cart.username);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [status, setStatus] = useState("aktiv");
const history = useHistory();
const usernameChangeHandler = (event) => {
setTitle(event.target.value);
};
const descriptionChangeHandler = (event) => {
setDescription(event.target.value);
};
const statusChangeHandler = (event) => {
setStatus(event.target.value);
};
const newTaskHandler = (event) => {
event.preventDefault();
console.log(`title: ${title} description ${description} status: ${status}`);
dispatch(
cartActions.addTask({
id: uuid(),
username,
title,
description,
status,
})
);
history.push("/");
};
return (
<div>
<div className='main-introduction'>
<h2 >Hello {username}!</h2>
<h1>Milyen feladatot szeretnél elvégezni?</h1>
</div>
<form onSubmit={newTaskHandler}>
<label htmlFor="title">A feladat címe</label>
<br></br>
<input
id="title"
type="text"
value={title}
onChange={usernameChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="description">A feladat leírása:</label>
<br></br>
<input
id="description"
type="text"
value={description}
onChange={descriptionChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="status">A feladat állapotta:</label>
<br></br>
<div onChange={statusChangeHandler}>
<input type="radio" value="aktiv" name="status" required /> Aktív
<input type="radio" value="kesz" name="status" /> Teljesített
</div>
<br></br>
<button type="submit">Hozzáad</button>
</form>
</div>
);
}
package.json
"dependencies": {
"#reduxjs/toolkit": "^1.5.0",
"#testing-library/jest-dom": "^5.11.6",
"#testing-library/react": "^11.2.2",
"#testing-library/user-event": "^12.5.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "5.3.0",
"react-scripts": "4.0.1",
"react-spring": "^9.4.3",
"react-transition-group": "^4.4.2",
"use-sound": "^4.0.1",
"uuidv4": "^6.2.12",
"web-vitals": "^0.2.4"
},
I think found the answer. with help for Drew Reese
Organize my route components different file.
It cause the right rendering.
Besides that using location.pathname instead location.key.
If you have better solution please write here
Thank you
App.js
import {
Fragment,
useEffect,
Suspense,
} from "react";
import { useSelector, useDispatch } from "react-redux";
import Notification from "./shared/UIElements/Notification";
import { sendCartData, fetchCartData } from "./store/cart-actions";
import MainNavigation from "./Layout/Navigation/MainNavigation";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import "../src/scss/styles.css";
import {
BrowserRouter as Router,
useLocation,
} from "react-router-dom";
import AuthenticationApp from "./AuthenticationApp";
import TaskApp from "./TaskApp";
let isInitial = true;
function App() {
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
const logged = useSelector((state) => state.cart.logged);
const location = useLocation();
useEffect(() => {
dispatch(fetchCartData());
console.log(`fetch usefeect`);
}, [dispatch]);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
if (cart.changed) {
dispatch(sendCartData(cart));
}
}, [cart, dispatch]);
return (
<Router>
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainNavigation />
<TransitionGroup>
<CSSTransition
timeout={1250}
classNames="fade"
pathname={location.pathname}
>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
{!logged && <AuthenticationApp />}
{logged && <TaskApp />}
</Suspense>
</CSSTransition>
</TransitionGroup>
</Fragment>
</Router>
);
}
export default App;
AuthenticationApp.js
import { Route, Redirect, Switch, useLocation } from "react-router-dom";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import { Suspense } from "react";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import Auth from "./Auth/page/Auth";
const AuthenticationApp = ({}) => {
const location = useLocation();
return (
<TransitionGroup>
<CSSTransition timeout={1250} classNames="fade" key={location.pathname}>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
<Switch>
<Route path="/" exact>
<Auth />
</Route>
<Redirect to="/" />
</Switch>
</Suspense>
</CSSTransition>
</TransitionGroup>
);
};
export default AuthenticationApp;
TaskApp.js
import { Route, Redirect, Switch, useLocation } from "react-router-dom";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import { Suspense } from "react";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import TaskMain from "./tasks/page/TaskMain";
import NewTask from "./tasks/page/NewTask";
import UpdateTask from "./tasks/page/UpdateTask";
import TaskFilter from "./tasks/page/TaskFilter";
const TaskApp = () => {
const location = useLocation();
return (
<TransitionGroup>
<CSSTransition timeout={1250} classNames="fade" key={location.pathname}>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
<Switch>
<Route path="/" exact>
<TaskMain />
</Route>
<Route path="/tasks/new" exact>
<NewTask />
</Route>
<Route path="/tasks/update/:id" exact>
<UpdateTask />
</Route>
<Route path="/items" exact>
<TaskFilter />
</Route>
<Redirect to="/" />
</Switch>
</Suspense>
</CSSTransition>
</TransitionGroup>
);
};
export default TaskApp;
I am using React-Router v4. However, whenever my applications Redux state is updated, React-Router's Switch component is updating and re-mounting the current route.
I define my HashRouter at in the main index.js file, like so:
/* global document */
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { HashRouter } from 'react-router-dom'
import App from './screens/app/index'
import registerServiceWorker from './registerServiceWorker'
import './styles/main.css'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>,
document.getElementById('root'),
)
registerServiceWorker()
This is my component using the Switch component:
import PropTypes from 'prop-types'
import React from 'react'
import { Route, Switch } from 'react-router-dom'
const propTypes = {
routes: PropTypes.arrayOf(PropTypes.shape({
path: PropTypes.string,
name: PropTypes.string,
container: PropTypes.object,
exact: PropTypes.bool,
})).isRequired,
}
const Router = ({ routes }) => (
<Switch>
<div className="container">
{routes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={() => route.container}
/>
))}
</div>
</Switch>
)
Router.propTypes = propTypes
export default Router
And finally, I setup my store here:
import { createStore } from 'redux'
import reducer from './reducers/index'
export default createStore(reducer)
I was thinking possible issues could be:
There an issue with my Redux setup that is causing a collision with the redux store
The Routes aren't defined correctly
I suspect this is the line that remounts the component everytime
component={() => route.container}
try
const Router = ({ routes }) => (
<Switch>
<div className="container">
{routes.map(route => {
const routerComp = () => route.container
return (
<Route
key={route.path}
path={route.path}
exact={route.exact}
component={routerComp}
/>
)
})}
</div>
)
I upgraded meteor and I started receiving deprecation warning for createContainer(). As a result, I've tried to implement withTracker however now I'm getting Component(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.. I'm not sure what I'm missing here, can someone point out my error.
Path: App.jsx
import { Meteor } from 'meteor/meteor';
import React from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { withTracker } from 'meteor/react-meteor-data';
// IsCandidate Spefic Routes
import TestContainer from '../../containers/candidate/TestContainer';
const App = appProps => (
<Router>
<ScrollToTop>
<div className="bgColor">
<NavBar {...appProps} />
<Grid className="main-page-container">
<Switch>
{/* candidate routes */}
<IsCandidate exact path="/candidate/testpage/:id" component={withTracker(TestContainer)} {...appProps} />
{/* IsPublic routes */}
<Route render={function () {
return <p>Page not found</p>;
}}
/>
</Switch>
</Grid>
</div>
</ScrollToTop>
</Router>
);
App.propTypes = {
loggingIn: PropTypes.bool,
isCandidate: PropTypes.bool
};
export default createContainer(() => {
const loggingIn = Meteor.loggingIn();
return {
loggingIn,
isCandidate: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isCandidate'),
};
}, App);
Path: IsCandidate.jsx
import React from 'react';
import PropTypes from 'prop-types'; // ES6
import { Route, Redirect } from 'react-router-dom';
const IsCandidate = ({ loggingIn, isCandidate, component: Component, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (loggingIn) return <div />;
return isCandidate ?
(<Component loggingIn={loggingIn} isCandidate={isCandidate} {...rest} {...props} />) :
(<Redirect to="/login" />);
}}
/>
);
IsCandidate.propTypes = {
loggingIn: PropTypes.bool,
isCandidate: PropTypes.bool,
component: PropTypes.func
};
export default IsCandidate;
Path: Testcontainer.jsx
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Test } from '../../../api/test/test';
import TestPage from '../../pages/candidate/TestPage';
export default TestContainer = withTracker(({ match }) => {
const testHandle = Meteor.subscribe('test', match.params.id);
const loadingTest = !testHandle.ready();
const testCollection = Test.findOne(match.params.id);
const testExist = !loadingTest && !!testCollection;
return {
loadingTest,
testExist,
testCollection: testExist ? testCollection : {}
};
}, TestPage);
Update
export default withTracker(() => {
const loggingIn = Meteor.loggingIn();
return {
loggingIn,
isCandidate: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isCandidate'),
isEmployer: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isEmployer'),
isAdmin: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isAdmin')
};
})(App);
In App.jsx you import withTracker but use createContainer.
withTracker takes only one argument (your reactive function) and wraps your child component where createContainer took 2 arguments (function and component).
createContainer(fn, C);
withTracker(fn)(C);
EDIT
Remove the withTracker call in App.js from this line:
<IsCandidate exact path="/candidate/testpage/:id" component={withTracker(TestContainer)} {...appProps} />
so it becomes
<IsCandidate exact path="/candidate/testpage/:id" component={TestContainer} {...appProps} />
How about it?