Routing to a new component from react-bootstrap-table-next? - javascript

I have a table of coding problems. When a user clicks on the name of the problem, I want the app to route to a page for a Problem component with props.
Right now I am trying to do so by using formatter and then creating a Route with react-router-dom. However, the component just opens inside of the table, instead of opening the component in its own page.
function nameFormatter(cell, row) {
return (
<>
<BrowserRouter>
<Switch>
<Route
path={"/problem/" + cell}
render={() => (
<Problem
id={row.id}
/>
)}/>
</Switch>
<NavLink to={"/problem/" + cell}>{cell}</NavLink>
</BrowserRouter>
</>
);
}
For a better demo, here's my sandbox: https://codesandbox.io/s/optimistic-germain-jsc06?file=/src/index.js
I may be overthinking this, and I may be tired, but any help would be appreciated.

Your routes are structured somewhat incorrectly. Generally speaking, your Home app should really be nothing but a bunch of route definitions. That way you create this "top level" router of sorts. You, on the other hand, make your <BrowserRouter> children subordinate to that table.
This is how your Home element should look:
const Home = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path={"/"}>
<ProblemTable />
</Route>
{problems.map((item) => (
<Route
key={item.id}
path={"/problem/" + item.name}
render={() => <Problem id={item.id} />}
/>
))}
</Switch>
</BrowserRouter>
);
};
So <ProblemTable /> is that table of problems and is rendered at / and the rest of the routes are defined right below it.
Here is a Sandbox for you.

Related

React Router: Routes within routes

I am building a React app that has a static marketing site and a dynamic app. I am using a combination of React Router and hooks to separate the two and ensure proper routing throughout.
To begin with, I want users who are logged in to be taken directly to the app when they hit the root ("/") and to the static/marketing site when not logged in. The main marketing site home page has a nav bar that allows users to access other routes such as "/about", whereas the app has a separate nav bar for app navigation.
The problem is, while the authentication based routing for the root route seems to be working, and I can navigate to other routes specified in my top-level file, the routes that are included within my static/marketing site are not accessible.
Top-level/index.js
const routing = (
<Provider store={store}>
<Router>
<NavWrapper />
{/* <Switch> */}
<Route exact path="/" component={AuthWrapper} />
<Route path="/signup" component={SignUp} />
<Route path="/login" component={Login} />
{/* </Switch> */}
</Router>
</Provider>
)
ReactDOM.render(routing, document.getElementById('root'));
AuthWrapper.js (here useAuth() is a React hook that evaluates global Redux isAuthenticated state)
const AuthWrapper = (props) => {
return useAuth() ? <App /> : <Website />
}
export default AuthWrapper;
website/index.js
export const Website = () => {
return (
<>
{/* <SiteNav /> */}
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/about" component={AboutPage} />
<Route path="/how-it-works" component={HowItWorksPage} />
<Route path="/plans" component={PlansPage} />
<Route path="/press" component={PressPage} />
<Route path="/faq" component={FAQPage} />
<Route path="/legal" component={LegalPage} />
<Route path="/for-dieticians" component={DietitiansPage} />
<Route path="/for-trainers" component={PTsPage} />
</Switch>
</>
)
};
In my app, when I enter localhost:3000/ (unauthenticated) I am correctly taken to my home/landing page. However, if I try to use the navigation (or type in any subroutes) to access "/about" for example, I get a blank screen (other than the nav bar). In the React dev tools, the component isn't even rendering.
I think this is the issue:
<Route exact path="/" component={AuthWrapper} />
You want Authwrapper to be shown for about as well, right? But the above only matches "/" exactly.
You should enable the switch statement, but move the AuthWrapper Route to the bottom and then let it have this path: "/:rest*" (maybe "/*" works too, haven't used react-router in a while).
That way, it will use the Route if the first 2 don't match.

What's the proper way to redirect with React routing when a user is not logged into the application?

In a React SPA, I have a collection of "pages" under the /src/pages/ folder.
The entry point page is an index.js file under the /src/ folder, where I define a router const like this:
const routing = (
<Router>
<div>
<Switch>
<Route path="/signIn" component={SignIn} />
<Route exact path="/" component={Homepage} />
<Route path="/page1" component={Page1} />
<Route path="/page2" component={Page2} />
<Route path="/page3" component={Page3} />
<Route component={NotFound} />
</Switch>
</div>
</Router>
It works great and all. All pages are navigable like "https://mysuperapp.com/page2" and it will render the Page2 React component.
Concerns arise when I incorporate user session management (log in, log out). If a user is not logged in the app, all pages should automatically redirect to the /signIn page. And viceversa, if a user is already logged, if the /signIn page is accessed, it should automatically redirect to the root homepage.
Right now I have implemented this by adding the following code to all the pages, right after the render() method is declared in the component, like this:
class Page2 extends React.Component {
render() {
if (UserProfile.getUserSessionStatus() !== "logged") {
this.props.history.push("/signIn");
}
}
return (
JSX code to be rendered here...
...
This works, but it feels like a cheap workaround used by someone who is just learning React, not by a professional.
For a proof of concept it works, but I would never dare to use such a thing in a production environment.
So, what's the right, best-practices-aligned way to accomplish this?
One possible approach is to create a Higher Order component(HOC) and use it for protecting any routes that require login.
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
UserProfile.getUserSessionStatus() === "logged"
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
And then use like this
.....
<PrivateRoute path='/page1' component={Page1} />
.......
Hope this helps!

Returned React-Router <Route> doesn't work in switch

I wrote my application in a very modular way in the way that each package (section of the application) is a package and the only thing i'm returning from each package is a <Route to="/dest" component="something" />.
The problem that i'm having right now is that, this method of returning are not really working in and that's how i tested it:
The initial setup was something like this :
<Switch>
<Dashboard {...rest} />
<Users {...rest} />
<Quiz {...rest} />
</Switch>
And the Dashboard component is something like this :
...
return (
<Route exact path="/" render={ props => (
<Dashboard {...props} {...stuff} />
)}/>
)
and almost the same thing for User and Quiz components, which are directly being imported from their module packages.
when i run the application, i can access Dashboard & Users but Quiz is not being rendered.
if i change the order and put quiz on top of <Users /> it will work. which means it only can render one after first exact route.
but instead, if i take all those code and write the Routes in the switch itself without referencing to another component, it works like a charm.
<Switch>
<Route exact path="/" render={ props => (
<div>demo</div>
)}/>
<Route path="/dashboard" render={ props => (
<div>demo</div>
)}/>
<Route path="/users" render={ props => (
<Users />
)}/>
<Route path="/quiz" component="Users"/>
</Switch>
I've tried both component, and render in route, this one is just a sample
any suggestion on why is this happening ?
UPDATE 1
I haven't managed to figure out what the problem is and what's causing it, but i found a way around it by creating a component that returns a Switch and direct Routes as children. then i exported the Switch to my main app.
<Switch>
<Dashboard {...rest} />
<Users {...rest} />
<Quiz {...rest} />
</Switch>
This will not work because Switch expects Routes to be direct children. It cannot perform its logic on arbitrary wrapper components.
I not sure if that would work but you would have to directly export Route from the module:
Dashboard.js
export default (
<Route exact path="/" render={ props => (
<Dashboard {...props} />
)}/>
)
but when you do something like this:
export default (stuff) => (
<Route exact path="/" render={ props => (
<Dashboard {...props} {...stuff} />
)}/>
)
Notice that you are not exporting a Route but a functional component that renders Route.
I personally think that you should only export purely components to be rendered without Routes and leave that up to library consumer.

Having troubles with react-semantic-ui and router

so heres the sample:
simple menu/header JSX:
<Menu fixed='top' inverted>
<Container>
<Menu.Item header>Simple Blog</Menu.Item>
<Menu.Item name='home' as={Link} to='/'
active={this.state.activeItem === 'home'}
onClick={this.handleItemClick}>Home</Menu.Item>
<Menu.Item name='create-p' as={Link} to='/create-post'
active={this.state.activeItem === 'create-p'}
onClick={this.handleItemClick}>Create post</Menu.Item>
</Container>
</Menu>
then I have basic index.js:
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
<ApplicationMenu/>
<Container style={{marginTop: '5em'}}>
<Route exact path="/" component={Posts}/>
<Route exact path="/home" component={CreatePost}/>
</Container>
</div>
</BrowserRouter>
</Provider>
, document.querySelector('.start'));
It renders and looks fine. When I click one of menu items I can see the URL changing, but no action is taken besides that. Router does not render new component. If I select the changed URL and click enter, it renders the proper one, but it looks like it does not react on url changes provoked by semantic ui component.
For debug purposes I prototyped these menu items just using clean html and css (pure sui, not react one) and it worked.
Did anyone have have similar problems and managed to figure it out?
Thanks for any answer.
This is a common problem to react-router-dom.
React works the way that it rerenders everytime either:
the properties of a component change
the internal state changes
If you change the URL of the page, you do not trigger a rerender of that component. Therefore, your index.js won't be rerendered.
A simple solution to this problem is to wrap your Component with a withRouter.
So your new index should look like this:
import { withRouter } from 'react-router-dom';
ReactDOM.render(withRouter(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<div>
<ApplicationMenu/>
<Container style={{marginTop: '5em'}}>
<Route exact path="/" component={Posts}/>
<Route exact path="/home" component={CreatePost}/>
</Container>
</div>
</BrowserRouter>
</Provider>)
, document.querySelector('.start'));
Now when the URL changes, the higher order component "withRouter" notices it and rerenders the component in it, so your correct component will be rendered.
As documented here: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
If you want to know more about the problem, read this GitHub issue: https://github.com/ReactTraining/react-router/issues/5037
Your Route path don't match your Item Link to.
Try changing your routes to:
<Switch>
<Redirect exact path="/" to="/home">
<Route exact path="/home/" component={Posts}/>
<Route exact path="/create-post/" component={CreatePost}/>
</Switch>
BTW: Take a look at react-router's <NavLink> to use instead of <Link> for your Navbar items.

Keep navigation bar on route switch

I have a problem with my react components.
Basically my interface should be an SPA, built with ReactJS.
During the implementation of the authentication with auth0-js I also implemented some routes.
The layout looks like this:
Whenever I click now on the "Recipes" link, it should redirect to the route "/recipes".
However, when I implement the route with <Route path="/recipes" component={Recipes} /> it only renders what is actually returned in the Recipes component. I know that this is correct from what react does.
Now, I want to keep the navigation bar but only want to exchange the component below, so I want to change what is below the navigation bar like I did in App.js.
How can I achieve this? Is something about the routes, or components not correct?
I do not want to re-render always everything. I also want to keep the style of the whole page. Is there a way to do this?
The whole code can be found here.
I agree with Jyutzio in that you need to move the Navigation component above the child routes to only change to content of the child routes.
In order to have the Navigation bar update with logged in / logged out state you may want to consider implementing redux. I have a project with nearly the exact requirements as yours - a navigation header that is static.
In my header I have import { connect } from 'react-redux';
At the bottom of the component I use connect before exporting:
function mapStateToProps(state) {
return { authenticated: state.auth.authenticated };
}
Header = connect(mapStateToProps)(Header);
export default Header;
Then this allows me to check the "authenticated" piece of state and render accordingly.
renderLogoutButton() {
if(this.props.authenticated) {
return(
<li><a onClick={...}>Logout</a></li>
);
} else {
return(
<li><a onClick={...}>Login</a></li>
);
}
}
You will need to setup a reducer, but there are many resources explaining redux setup.
The router (simplified) I have set up as follows:
import Admin from './index';
...
<BrowserRouter>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/" component={Admin} />
</Switch>
</BrowserRouter>
index:
import AdminRouter from './admin/admin_router';
...
<div>
<Menu />
<div>
<AdminRouter />
</div>
</div>
admin_router:
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/details" component={AdminDetails} />
<Route component={PageNotFound} />
</Switch>
</div>
If you place your Navigation component above the switch, it will solve your problem.
const Routes = () => (
<Router history={history} component={Home}>
<Route component={Navigation}/>
<Switch>
<Route exact path="/" render={props => <Home auth={auth} {...props} />} />
<Route path="/home" render={props => <Home auth={auth} {...props} />} />
<Route
path="/callback"
render={props => {
handleAuthentication(props);
return <Callback {...props} />;
}}
/>
<Route component={NotFound} />
</Switch>
</Router>
);

Categories