Can't get Params from id routes - reactjs

I'm stuck with a problem in my project. I'm trying to show a component and work with the this.props.match.params, but no matter what I do I get undefined.
Routes:
const App = () => (
<BrowserRouter>
<Fragment>
<Header/>
<main>
<Switch>
<Route path="/show/:id" component={Show}/>
<Route path="/" component={Home}/>
</Switch>
</main>
</Fragment>
</BrowserRouter>
);
export default App;
then I have a handler on my home route:
async handleSubmit(searchId) {
const id = await DwellingService.findSiocId(searchId);
if (id) {
this.props.history.push(`/show/${id}`);
}
}
and finally on my show component
componentDidMount() {
console.log(this.props.match.params)
const {id} = this.props.match.params;
if (id) {
this.props.requestFindDwelling(id);
}
}
So I have been researching and I think is not a react router problem, first when I try to access the routes by typing them I was getting unexpected > on my bundle.js which was solved adding <base href="/" /> on the index.html.
Now my component is rendering ok by the console.log of the show component is giving me this:
isExact:false
params:{}
path:"/show"
url:"/show"
When I started the project to be able to use browserhistory and not getting error by refreshing the page I had to add this to my index file:
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, './public/index.html'), function(err) {
if (err) {
res.status(500).send(err);
}
});
});
For the kind of error I get I'm supposing the route is not being found and is redirecting me to /show.

<Switch>
<Route path="/show/:id" component={Show}/>
<Route path="/" component={Home}/>
</Switch>
This will never render Home as Switch renders first thing that matches and / will match always the first route. Not sure if this will fix the problem but try and let me know:
<Switch>
<Route exact path="/" component={Home}/> // exact is important
<Route path="/show/:id" component={Show}/>
</Switch>

Related

React router route loader not working on nested components

Am using react router v6 and i would like to use the new loader to load the data before the component loads. So i have the following
In my index.js
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="*"
loader={async ({ params }) => {
console.log("index loader log"); //this logs
return true;
}}
element={<App />}
> </Route>
)
);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<RouterProvider router={router} />
);
in my app component i have nested routes like
const App = () => {
return (
<>
<Routes>
<Route path="auth/*" element={<AuthLayout/>}/>
<Route path="about"
loader={async ({ params }) => {
console.log("about child loader log"); //this doesnt log
return true;
}}
element={<AboutPage/>}/>
</Routes>
<h1>Testing app</h1>
</>
);
}
On the app component the loader on the Route path="about" does not console.log when i visit the about route but the component is rendered. What am i missing for the loader to work on the child route.
Based on some basic testing it seems that in order for the new RRDv6.4 data APIs to work you need to specify the complete routing configuration in the createBrowserRouter function.
There does however appear to already be an issue filed with #remix-run/react-router for this behavior as a reported bug, so you may want to follow it if it ever addressed/resolved. (I suspect it was you since the name is "geoffrey" and the timing is coincidentally about an hour ago around the same time as this post)
This above issue has since been closed with comment:
Descendant <Routes> trees do not participate in data loading
(https://reactrouter.com/en/main/components/routes) since they cannot
be known ahead of render-time. You'll need to lift your descendant
route definitions up into the routes you pass to createBrowserRouter.
The relevant information regarding the descendent routes and the new Data API can be found in the Routes documentation in a note.
Note:
If you're using a data router like createBrowserRouter it is
uncommon to use this component as it does not participate in data
loading.
Hoist the entire route declaration to the parent creating the data router. The following does work with the loader function for the "/about" route and About component.
const router = createBrowserRouter(
createRoutesFromElements(
<Route
path="*"
loader={({ params }) => {
console.log("index loader log");
return "This is the App";
}}
element={<App />}
>
<Route path="auth/*" element={<AuthLayout />} />
<Route
path="about"
loader={({ params }) => {
console.log("about child loader log");
return "this is the about page";
}}
element={<AboutPage />}
/>
</Route>
)
);
The App component should render an Outlet for the nested routes to render their content into.
import { Outlet } from 'react-router-dom';
const App = () => {
return (
<>
<h1>Testing app</h1>
<Outlet />
</>
);
};

Convert react-router to v4: Redirect all routes to include lang

We've localized our app and now I need to include the lang in all routes, I've come across code that would do what I want but have trouble doing the same in react-router v4
Here is the code I'm referencing
How to set and handle language in react-router from?
Here is the sample code I'm working with to get it functional before integrating it in our app:
// add lang to url if it's not present
function langRedirect(props) {
console.log({ props });
const defaultLang = 'en';
const redirectPath = `/${defaultLang}${props.history.location.pathname}`;
console.log(redirectPath);
props.history.replace({ pathname: redirectPath, });
}
....
<Switch>
<Route path="/:lang/" render={() => <h1>testing... does nothing</h1>}>
<Route path="/:lang/test" render={() => <h2>Test</h2>} />
<Route path="/:lang/test2" render={() => <h2>Test2</h2>} />
</Route>
<Route path="*" render={langRedirect} />
</Switch>
What happens here is if I go to a route like /test the switch will still catch the /:lang/ route even if there is no / at the end which prevents going to the route with langRedirect. Seems like a different behaviour in v4.
Well, after spending hours on this I happened to figure it out right after posting the question...
I had to add a * to the route like so <Route exact path="/:lang/*" render={() => <h1>Test</h1>}>
Here is the working code:
// add lang to url if it's not present
function langRedirect(props) {
console.log({ props });
const defaultLang = 'en';
const redirectPath = `/${defaultLang}${props.history.location.pathname}`;
console.log(redirectPath);
props.history.replace({
pathname: redirectPath,
});
}
...
const TestingRoutes = () => {
return (
<Switch>
<Route exact path="/:lang/*" render={() => <h1>Test</h1>}>
<Route path="/:lang/test" render={() => <h2>Test</h2>} />
<Route path="/:lang/test2" render={() => <h2>Test2</h2>} />
</Route>
<Route path="*" render={langRedirect} />
</Switch>
);
};

Keycloak json configuration file, React and Router 4

I am using React (16.3.2) with TypeScript (2.8.3), Keycloak-js (3.4.3) and React Router 4 (4.2.2) together. Here is the Keycloak init:
const auth = Keycloak('./../keycloak.json');
const init = () => {
return auth.init({ onLoad: 'login-required', checkLoginIframe: false });
};
The keycloak.json file is stored in public folder
I do Keycloak initialization before ReactDOM.render method:
import { init } from './auth';
init()
.success((authenticated: boolean) => {
if (authenticated) {
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root') as HTMLElement
);
} else {
console.log('not authenticated');
}
})
.error(() => {
console.log('failed to initialize');
});
Then the App (ThemeProvider comes from styled-components):
const App: React.SFC<Props> = ({ theme }) => {
return (
<BrowserRouter>
<ThemeProvider theme={theme}>
<Switch>
<Redirect from="/" exact={true} to="/books" />
<Route path="/books" component={BooksList} />
<Route component={Error404} />
</Switch>
</ThemeProvider>
</BrowserRouter>
);
};
Then the BooksList:
const BooksList: React.SFC<RouteComponentProps<void>> = ({ match }) => {
return (
<ColumnView>
<Switch>
<Route path={match.url} component={List} />
</Switch>
<Switch>
<Route path={match.url} exact component={EmptyView} />
<Route path={match.url + '/details/:id'} component={BookDetails} />
<Route component={Error404} />
</Switch>
</ColumnView>
);
};
When I open my website on URL localhost:3000 everything works as it should. Keycloak renders a login page and I can navigate through the whole website. The problem appears when I want to enter a different URL by typing it to the browser, for example localhost:3000/books/details/11. Suddenly Keycloak starts to search for the keycloak.json file in a very different directory - not localhost:3000/keycloak.json but localhost:3000/books/details/keycloak.json.
The problem seems to be non existent when I write the localization of the configuration file as:
const auth = Keycloak('./../../../keycloak.json');
Where the number of '../' depends on how much nested my router is. This fixes everything.
So the solution is quite easy - I had to delete the single dot in front of the initialization URL to make the path direct not relative:
const auth = Keycloak('/../keycloak.json');

Reactjs router onEnter hook authentication cause infinite loop

I'm trying to redirect logged in user on dashboard page.
Here is my code:
function requireAuth(nextState, replace) {
if(isLoggedIn()) {
console.log("user is logged in");
replace('/dashboard');
} else {
replace('/');
}
}
const Root = () => {
return (
<div className="container">
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route onEnter={requireAuth}>
<Route path="dashboard" component={AllEvents}/>
</Route>
</Route>
</Router>
</div>
)
}
When the user is logged in, my application is running into a loop with a requireAuth method.
Here is the screenshot of the console.
I've already considered two similar questions on StackOverflow which are:
react Maximum call stack size exceeded
React-router, onEnter cause infinite loop with authentication
I've tried both of them, but unfortunately, those examples didn't help me. (I'm also a beginner in React)
Please, tell me what is wrong with my code?
You get an infinite loop because if the user is logged it always redirect him to /dashboard and repeat the redirecting process starting from / and again hitting requireAuth.
Try:
function onlyUnAuthenticated(nextState, replace, callback) {
if(isLoggedIn()) {
replace('/dashboard');
}
callback();
}
function onlyAuthenticated(nextState, replace, callback) {
if(!isLoggedIn()) {
replace('/');
}
callback();
}
const Root = () => {
return (
<div className="container">
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={onlyUnAuthenticated}>
<Route path="dashboard" component={AllEvents} onEnter={onlyAuthenticated}/>
</Route>
</Router>
</div>
)
}
I think that you will have to use callback in the hook.

how to reset react-router on page reload

using "react-router": "^3.0.2",
When I do a page reload I need it to go to the default view, which it is currently doing. However, the actual route that is in the history is still the same as the one from where I did the refresh at. So that component is remounting when it shouldn't be.
routing history
export const browserHistory = useRouterHistory(useBeforeUnload(createHistory))()
routes
<Router history={browserHistory}>
<Route path='/' name='Auth' component={Auth}>
<IndexRoute
component={Dashboard}
onEnter={(nextState, replace) => replace('/login')} />
<Route path='dashboard' name='Dashboard' component={Dashboard} />
<Route path='resources' name='Resources' component={Resources} />
<Route path='users' name='Users' component={UsersContainer} />
<Route path='user/:params' name='User' component={UserContainer} />
</Route>
<Route path='/' name='NoAuth' component={NoAuth}>
<Route path='login' name='Login Page' component={Login} />
</Route>
</Router>
This is how I check to see if the user still has a valid session token, and how I reroute to the dashboard. Not sure if I am doing this the best way.
const _checkAuth = () => {
if (profile) {
const res = JSON.parse(profile)
store.dispatch({ type: types.LOGIN_IDENTITY_SUCCESS, res })
console.log(browserHistory.getCurrentLocation().pathname)
router.replace('/dashboard')
}
}
_checkAuth()
if you want to push onto a new route with react-router you can use the .push method to push another route onto the stack, this won't remove the first route in the history if that's what you are looking for, but it will correctly push the new route into react-router
const _checkAuth = () => {
if (profile) {
const res = JSON.parse(profile)
store.dispatch({ type: types.LOGIN_IDENTITY_SUCCESS, res })
console.log(browserHistory.getCurrentLocation().pathname)
browserHistory.push('/dashboard');
}
}
_checkAuth()
Just replace router.replace('/dashboard') with browserHistory.push('/dashboard')

Resources