Why am I outside the <Router> after rendering inside it? - javascript

Im building a Reactjs App. I don't understand why doing this is wrong and lead me to this error :
Error: Invariant failed: You should not use <Redirect> outside a <Router>
Here is the code :
A simple router in index.js
ReactDOM.render(
<Router>
<Switch>
<Route exact path="/" component={(props) => <AppContainer><HomePage {...props}/></AppContainer>}/>
<Route exact path="/register" component={(props) => <AppContainer><RegisterPage1 {...props}/></AppContainer>}/>
</Switch>
</Router>, document.getElementById('root')
);
The AppContainer is just a simple div with id :
export default function AppContainer({children}) {
return (
<div id="appContainer">{children}</div>
);
}
Inside RegisterPage1, I trigger a Render on "appContainer" after some process. This should render a new component inside the router.
ReactDOM.render(<RegisterPage2 data={res}/>, document.getElementById('appContainer'));
Inside RegisterPage2, I'm using and I get the above error.
if (redirectHome) return (<Redirect to={"/"}/>)
else {
return (
//RegisterPage2Components
)
}
Since I'm rendering inside the Router, I dont understand why I can use Redirect in RegisterPage1 and cannot in RegisterPage2. I checked in RegisterPage2 and the router is still here (by adding some random text between the Router and the Switch).
If rendering like this is wrong, what is the correct way without adding a new route ?
Codesandbox :
https://codesandbox.io/s/autumn-leftpad-7s5sy?file=/src/index.js

Since I'm rendering inside the Router, I dont understand why I can use
Redirect in RegisterPage1 and cannot in RegisterPage2.
React Dev Tools can help you here
As you can see at the bottom part of the image, RegisterPage2 is not inside a Router component. Compare it with the topmost portion of the image, that React App is wrapped inside a BrowserRouter. You will realize why these errors occur.
If rendering like this is wrong, what is the correct way without
adding a new route?
It is with great difficulty this requirement you are asking. It will be hard to traverse between two React Apps using react-router if they don't have a common Router parent. I suggest you resort to the classic server side http request (example anchor tags or window.href.location) instead of using Single Page Application technologies for this feature you are developing

That issue is cause by this line
ReactDOM.render(
<RegisterPage2 data={res}/>,
document.getElementById('appContainer')
);
This line is supposed to render the RegisterPage2 component as the Root component of your React app.
And in the RegisterPage2 you are using Redirect by assuming the RegisterPage2 has already a parent component which has already use the react- router-dom Router Component. Because It's render as the Root Component without is beeing wrap in the Router Component that is the reason you are getting that error.

Related

React access a variable from child component

I have a main layout for my app and all components are rendered inside it, but this main layout needs to access a variable PageTitle from it's child components, I have looked in react documentation and come up with React Context, but I could'nt figure out how to use it in this case, sorry I am new to React.
Every component rendered by routes has a variable called PageTitle, the MainLayout should use this variable to render current page title on the top of the app, these are all functional components.
<MainLayout>
<Route path='/' exact component={HomePage} />
<Route path='/invoice' exact component={Invoice} />
</MainLayout>
Update:
I can create a context and store the value like this, but I couldn't figure out how to change it in child components.
Also I think this is a bit overkill, and there should a better solution.
export const AppContext = React.createContext({ PageTitle: 'Home' });
<AppContext.Consumer>{value=>value.PageTitle}</AppContext.Consumer>

React router v4 prevent matching child routes

I encountered a problem with React Router v4 Switch component. I'm quite surprised that i couldn't find a relevant thread for this problem. A common Switch will look like this:
<Switch>
<Route path='/path1' component={Path1Component}/>
<Route path='/path2' component={Path2Component}/>
<Route exact path='/' component={Home}/>
<Route component={NotFound}/>
</Switch>
This means that when i'm on a path: '/' i get a Home component, on '/path1' i get a Path1Component and on path '/foobar' i get a NotFound component. And that is perfectly fine
However when i'm on '/path1/foobar' route i also get the Path1Component. This behaviour is not correct in every case - this time i do not want any nested routes for '/path1' route. '/path1/foobar' should get a NotFound component, any string, with '/' or without after '/path1' should return NotFound component.
What would be the preferred resolution to this problem? I could just add exact to every path, but wouldn't that be overbloating the code? I feel like that should be the default, but it's not the case.
Even on React Router v4 docs, like here. I see this problem - here '/will-match/foo' will also match. What are your thoughts?
There is a discussion here, but to make it short : it would break existing code. If this were to change, you'd have to do exact={false} if you want to match child routes without doing 'path1/child1'.

How to get params in component in react router dom v4?

I have this code:
<BrowserRouter>
<Route path="/(:filter)?" component={App} />
</BrowserRouter>
the filter param or '' on the root is suppose to be on App components' props base on the previous react router versions?
This is my code on my App:
const App = ({params}) => {
return ( // destructure params on props
<div>
<AddTodo />
<VisibleTodoList
filter={params.filter || 'all'} // i want to git filter param or assign 'all' on root
/>
<Footer />
</div>
);
}
I logged this.props.match.params on console but it has none? help?
I assume you are following the Redux tutorial on Egghead.io, as your example code seems to use what is defined in that video series. I also got stuck on this part trying to integrate React Router v4, but eventually found a working solution not far from what you have tried.
⚠️ NOTE: one thing I would check here is that you are using the
current version of react-router-dom (4.1.1 at the time of this
writing). I had some issues with optional filters in the params on
some of the alpha and beta versions of v4.
First, some of the answers here are incorrect, and you indeed can use optional params in React Router v4 without the need for regular expressions, multiple paths, archaic syntax, or using the <Switch> component.
<BrowserRouter>
<Route path="/:filter?" component={App} />
</BrowserRouter>
Second, as others have noted, React Router v4 no longer exposes params on route handler components, but instead gives us a match prop to use.
const App = ({ match }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={match.params.filter || 'all'} />
<Footer />
</div>
)
}
From here, there are two ways to keep your content in sync with the current route: using mapStateToProps or using the HoC withRouter, both solutions are already talked about in the Egghead series so I won't recapitulate them here.
If you are still having trouble, I urge you to check out my repository of the completed application from that series.
Here is the commit using the mapStateToProps solution
Here is the commit using the withRouter soluiton
Both of which seem to work fine (I just tested both of them).
React Router v4 does not accept a regex for the path. You won't find regular expressions covered anywhere in the documentation. Instead of a regex you can just create multiple routes inside the <Switch> component and the first one that matches will be rendered:
<BrowserRouter>
<Switch>
<Route path="/" component={App} />
<Route path="/:filter" component={App} />
</Switch>
</BrowserRouter>
You also have a bug in your App component. You get the params via the match property, not the params property (not tested, but this should be closer to what you want):
const App = ({match}) => {
return (
<div>
<AddTodo />
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
<Footer />
</div>
);
}
All of the above is covered in the React Router V4 docs on ambiguous matches
From the react-router documentation, props.match.params is where your parameteres are stored.
So to access the filter name, try this
const App = ({match}) => {
...
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
...
}
I know the question is about v4, but if sm looks for v5, we can use the useParams hook.
// /:filter
const {filter} = useParams();
To get the params in the path URL
//to your route component
<Route path={`${match.url}/upload/:title`} component={FileUpload}/>
=====
//Parent component
<Link to={`${match.url}/upload/css`}>
=====
//Child component
const {params: {title}} = this.props.match;
console.log(title);
result = css
I do not know why react router cannot detect my filter even though it's working properly, I resolved this problem by using location.pathname since it does the work for me. I think react router cannot detect my filter param because of regexp, I expected that so I put || to use all on root, but unfortunately for me I it cannot detect so I used location.pathname . I would appreciate suggestions on this since I am not sure with my path configuration on regexp.

react router this.props.location

I need a help with react-router v2+
I have to change class of navbar when route changed
for example for route /profile className will be "profile-header"
I tried to use this.props.location in navbar component but it shows undefined
Hope your help
Your navbar component (as you described it in your question) is probably not the route component, right? By route component I mean the one that you use in your react-router configuration that is loaded for a specific route.
this.props.location is accessible only on such route component, so you need to pass it down to your navbar.
Let's take an example:
Your router config:
<Router>
<Route path="/" component={App}>
// ...
</Router
Route component App:
class App extends React.Component{
// ...
render() {
return <Navbar location={this.props.location}/>
}
}
There could be a scenario where you may not have access to props.location to pass to the nav component.
Take for example - We had a header component in our project which was included in the routing switch to make it available to all routes.
<Switch>
<Fragment>
<Header/>
<Route path='..' component={...}/>
<Route path='..' component={...}/>
</Fragment>
</Switch>
In the above scenario there is no way to pass the location data to the Header component.
A better solution would be to us the withRouter HOC when a component is not being rendered by your router.
You will still have access to the router properties history, match and location when you wrap it in the withRouter HOC:
import { withRouter } from 'react-router-dom'
....
....
export default withRouter(ThisComponent)
react-router v4
From documentation:
<Route> component property should be used, when you have an existing component. <Route> render property takes an inline function, that returns html.
A <Route> with no path will always match.
Based on this, we can make a <Route> wrapper to any level of your html structure. It will always be displayed and have access to the location object.
As per your request, if a user comes to /profile page, the <header> will have profile-header class name.
<Router>
<Route render={({ location }) =>
<header className={location.pathname.replace(/\//g, '') + '-header'}>
// header content...
</header>
<div id="content"></div>
<footer></footer>
} />
</Router>
I couldn't solve it with the solutions given here and here is what worked for me:
I imported history into my component and assigned history.location.pathname to a variable which I later used for dynamic style manipulation.
In case you are rendering the component with pre-defined location.state values, first set your state with props.location.state then use your state data in your elements.

Does `react-native-router-flux` allow for parent components which render children?

I might have missed something in the docs here.
Does react-native-router-flux allow for a parent scene with a component, which would render the child scenes through it?
In code speak, for this routing:
<Scene key="home" component={ApplicationRoot}>
<Scene key="settings" component={Settings} />
</Scene>
the ApplicationRoot component would
class ApplicationRoot extends React.Component {
constructor() { .. }
render() {
return(
<View>{this.props.children}</View>
);
}
}
passing Settings through here.
The idea being I can have lifecycle functions within ApplicationRoot to handle app-level functionality (like checking if logged in, listening for log outs etc), that would apply to all nested routes.
If this isn't possible via a wrapping parent component, does anyone know of a way to achieve this?
Following the example in the official code base, I've gone for the switcher solution; a root initial component (always set to initial={true}) which will in turn be responsible for checking (in my case) Redux state and routing to the relevant pages.
Doesn't do exactly what I was after, as it won't be able to listen to state at an application level, but it does the job.

Categories