Control conditional rendering of router in next.js with feature flag - reactjs

I am migrating my React app from react-router-dom to next.js. Previously in react router, I was using a hook useFeatureFlag in my top level component to would return a boolean which I could use to condtionally render a page.
const App = (): JSX.Element => {
const { showCalendar } = useFeatureFlags();
return (
<Switch>
{showCalendar && <AppRoute path="/calendar" component={Calendar} />}
</Switch>
);
};
How can I do the same thing in next.js? Next.js doesn't have one component where all the pages are defined, but rather uses the pages/ folder structure to infer routes, so I'm not sure how to put a boolean infront of pages/calendar to control the rendering of the Calendar component.

Related

React router/lazy suspense + useTransition - How to stay on current page until next page is rendered?

At this point if someone can figure this out I would be willing to pay some money.
My question pertains to using React Router and the new React 18's useTransitions. At the current stage, lazy loading is implemented using React.lazy and React.Suspense around the routes with the fallback being some React component you choose. As people have noticed, there is 'flickering' etc. So now there is useTransitions. Does anyone have an implementation using these to make it so you can stay on the current rendered page until the next page is ready to load.
I have seen this post: React lazy/Suspens + React Router dont change route until component is fetched
But it does not seem to work. I'm using react-router v5 and react v18.2. I have seen many people ask something similar, but if someone could share their implementation, I believe it would benefit more than myself.
I appreciate your time to view this. Thank you.
You can try with route-level-code-split for that you need to create custom FallbackProvider and utils. Which helps you store your current component until the next page is not loaded yet.
Here is the example Demo
Source code Link
This worked for me: React lazy/Suspens + React Router dont change route until component is fetched
Note the updated React 18 version of SuspenseRouter.
My suggestion for React v18 + React-Router-Dom v6 + Webpack would be to use #loadable/component
https://www.npmjs.com/package/#loadable/component
https://github.com/gregberge/loadable-components
Replace this:
import React, {lazy, Suspense} from 'react'
import { Routes , Route} from "react-router-dom"
const Home = lazy(() => import(/* webpackChunkName: "routes-home" */ './routes/Home'))
export default function App(){
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/routes/home" element={<Home />} />
</Routes>
<Suspense />
)
}
With this:
import loadable from '#loadable/component'
const Home = loadable(() => import(/* webpackChunkName: "routes-home" */ './routes/home'))
export default function App(){
return (
<Routes>
<Route path="/routes/home" element={<Home />} />
</Routes>
)
}
P.S. /* webpackChunkName: "routes-home" */ is optional

useNavigate() issue in micro frontend app

I'm designing a MFE app called header. Implementation is something like below,
header.js
const headerApp = () => {
const navigate = useNavigate();
const logoClickHandler = () => {
navigate('/some-route'); // router v6
}
return(
...
<Logo onClick={logoClickHandler} />
)
}
App.js
I want to keep/use it like below
const App = () = {
return(
<div>
<HeaderApp /> // This component just uses useNavigation or <NavLink to='/some-route' />
</div
)
}
Problem is Header app doesn't have its own routing mechanism in it. It is just a separate app and to be more specific standalone component and just provides navigations among different MFE apps using useNavigate() OR <NavLink /> router feature.
Since, I'm using useNaviage() OR <NavLink />, react is asking me to wrap the component inside <Routes> (as shown below) which is unnecessary for my header app.
React Error
useNavigate() may be used only in the context of a <Router> component.
don't want to end up like below,
const App = () = {
return(
<div>
<Routes>
<Route path='/' element={ <HeaderApp /> } />
</Routes>
</div
)
}
NOTE : Routing is handled in separate app called container. Header only provides links for navigations.
React Router uses React Context, which is a way of passing information down through the React tree. Because of this, you only need to make sure you have at least one <Router> as a parent of whatever component is rendering <headerApp /> for this to work.
If this is not acceptable to you - you want your application to be used in non-React router contexts, for example - you may want to refactor your header application such that it either provides its own React Router instance or accepts the required methods and attributes through props.
It is not possible to use <NavLink /> or useNavigate() without one of the parents of <headerApp /> using Router />.

React Router and passing props to other component

I have a button in one component and on click I want to go to other component and pass to this component the state.
It's something like this:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import AddNewQuestionPage from 'AddNewQuestionPage';
class AddQuestions extends Component {
state = {
questions: []
};
routeChange = () => {
let path = `/admin/add-new-question`;
this.props.history.push(path);
}
render() {
return (
<>
<button onClick={this.routeChange}>
Add new question
</button>
<BrowserRouter>
<Route exact={true} path='/admin/add-new-question' component={AddNewQuestionPage}/>
</BrowserRouter>
</>
)
}
}
And it doesn't work. On click I go to add-new-question url but the component AddNewQuestionPage doesn't render. It works if I put Route not in AddQuestions component, but in App component. It's the main component of the whole app and using Switch, there are set also other routes.
However I don't know how I can pass the state questions to AddNewQuestionPage component if it's rendered from App component? I can't just do:
<Route path='/admin/add-new-question' render={(props) => <AddNewQuestionPage {...props} questions={questions} />
because it doesn't know what is "questions". Lifting the state up to the main component doesn't seem a good solution for me. I was searching and I can't find how to do it...
You should use the this keyword on the question you're passing to the component.
So something like this
<Route path='/admin/add-new-question' render={(props) => <AddNewQuestionPage {...props} questions={this.state.questions} />
The best way to use react-router dom is :
Always make as a parent component for all.
The best way to change the path by clicking on a sort of component (button ...) is You label
Please check this guide out : https://reacttraining.com/react-router/web/guides/quick-start

React Router nesting reloads page

I'm trying to set up nested routes in React using React Router so that the nested components load directly, however the page reloads when I attempt to go to a nested route.
Even the official example does the same - https://reacttraining.com/react-router/web/example/nesting The official example works as expected when it is opened in a new window.
One thing I noticed was that if I actually change the route from within one of the child route components the page does not reload. But this is bad practice and I want to change the route in the component that defines the routes.
Has something changed recently? How can I achieve nested routes changes without page reload?
Parent
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
// At route /home
const Parent = (props: RouteComponentProps<any>) => {
const changeRoute = () => {
props.history.push('/home/test'); // Reloads page
};
return (
<Router>
<Switch>
<Route component={Test} path="/home/test" />
<Route component={Default} />
</Switch>
<button onClick={changeRoute}>Click</button>
</Router>
);
};
export default withRouter(Parent);
Child
const Default = (props: RouteComponentProps<any>) => {
const changeRoute = () => {
props.history.push('/home/test'); // Does not reload page
};
return (
<button onClick={changeRoute}>Click</button>
);
};
export default withRouter(Default);
I'm using react-router-dom v5.1.2.
import {HashRouter as Router} from "react-router-dom";
change BrowserRouter to HashRouter and check ,it stops reload issue
The problem was that I was using <Router> in Parent even though Parent was itself within <Router> tags. Replacing <Router> with <div> in Parent fixed the issue.

Get react router path in props with redux ownProps

I'm trying to get the current path of the react router in a container so I can pass it to a child component that will change it's visibility filter.
More specifically, I'm trying to make a navigation menu highlight the currently active page.
I'm using react, redux, react-router, and react-router-redux so I can access the router state from the redux store.
From the docs for react-router-redux, it says to do something like this:
function mapStateToProps(state, ownProps) {
return {
id: ownProps.params.id,
filter: ownProps.location.query.filter
};
}
Here is my container component:
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import {
Segment as UISegment,
} from 'semantic-ui-react'
import NavMenu from '../components/NavMenu'
class MenuBar extends Component {
static propTypes = {
path: PropTypes.string.isRequired
}
render() {
const { path, } = this.props
return (
<UISegment>
<NavMenu activePath={path} />
</UISegment>
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
path: ownProps.route ? ownProps.route.path : "/"
}
}
export default connect(mapStateToProps)(MenuBar)
Inside the NavMenu component, a semantic-ui menu component will compare activePath with its own path and highlight the active button.
Everything seems to work in theory; when I click on the different parts of the menu, a ##router/LOCATION_CHANGE action is emitted. In the redux dev tools, I see the state changing. However, mapStateToProps is never called and this component is never re-rendered.
Any ideas? I thought about using the react methods like shouldComponentUpdate, but it seems that react doesn't even realize the state or props are changing.
First thing to note is that you are not actually accessing router state from the store. If you look at the react-router-redux docs, it actually warns against doing so
You should not read the location state directly from the Redux store. This is because React Router operates asynchronously (to handle things such as dynamically-loaded components) and your component tree may not yet be updated in sync with your Redux state. You should rely on the props passed by React Router, as they are only updated after it has processed all asynchronous code.
Your container is reading data from ownProps, which is just the props that are passed into that container component. The example in the react-router-redux docs that you are referencing only works for a top-level route component (a component that is passed as the component prop to a React Router Route component). React Router passes the router data into all route components.
In your case, MenuBar is a child of whatever your top level route component is. Your two options are to
Pass the data you want into MenuBar down from your route component.
Use React Router's withRouter higher order component to inject the values into MenuBar https://github.com/ReactTraining/react-router/blob/v3/docs/API.md#withroutercomponent-options
Also, I believe the value you are looking for is ownProps.location.pathname rather than ownProps.route.path
Some code for option 1, since I'm assuming MenuBar isn't nested too deeply in your component tree:
If your route config is
<Router history={browserHistory}>
<Route path="/" component={AppLayout}>
<Route path="about" component={About}/>
<Route path="users" component={Users}/>
<Route path="*" component={NoMatch}/>
</Route>
</Router>
your AppLayout would be something like
const AppLayout = ({ children, location }) => {
return (
<div>
<MenuBar path={ location.pathname } />
{ children }
</div>
)
}
and MenuBar would receive the data your are looking for.

Resources