I know that the Router component, say BrowserRouter creates a history object for keeping track of paths and so on, and that a Route component renders its view only when its path matches the current location, which I assume is read from the history. My question is how does the Route component get access to the history object that is created by the BrowserRouter. Is there some under the hood communication going on that is making this possible?
This is a context api 'magic'.
<BrowserRouter/> renders <Router/>:
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
<Router/> renders <RouterContext.Provider/>:
<RouterContext.Provider
children={this.props.children || null}
value={{
history: this.props.history,
<Route/> uses <RouterContext.Consumer> to provide access to data from provider.
Using context api allows to provide data/methods many levels down the tree without the need of passing down props expliticely (on each level). You can read more about this on docs or search for tutorials.
Related
I am working on a web app that is consisted of few pages and have configured routes with react-route-dom. On the pages, I have some shared components and I want them to be styled differently on other pages except the Home page.
How do I get this done? Any help?
Thanks
Check if pathname is path of home or else not you can use useLocation to get the pathname and add conditional styling or classes.
useLocation v5
useLocation v6
import React from 'react';
import {useLocation} from 'react-router-dom';
export const Common_Component=()=>{
const location=useLocation()
const pathname=location.pathname
return(
<div>
<div classes={pathname=="home"?"home_class":"other_class"}>
...
</div>
</div>
)
}
Or
You can pass boolean props to the component to let component know it should use home bases classes or other classes.
PS.
you will still need to do conditional check if props is true or false
useMatch you can use useMatch to match the current url with regex.
While it is always good to share code, I would recommend creating a new component for the homepage. Otherwise you are coupling your components. What happens if you want to add another specialisation for the whatever-component? You add another case! Your components grow and wire up more and more until you have one big pot of spaghetti. Hence, it makes sense in many situations to just create a new component for a new use-case.
I am building a React app with sagas for Redux and next.js. The main app.js file is handled as follows:
import { wrapper } from '../state/store';
const App = ({ Component, pageProps }) => (
<Layout>
<Component {...pageProps} />
</Layout>
);
export default wrapper.withRedux(App);
I can call and write to the state using dispatch for the pages in this app and some of their direct children. However, with others I get an error that is always something like ...
Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
I don't understand how the parent page and some children are fine, but others have problems. It seems like components must be registered somewhere to be in the provider as every new component that I add has problems.
If the app is not wrapped in a provider, why does it work in the main page and some children? If it is wrapped, why aren't some components seeing that?
There are X routes in the frontend made with React-router and each of them drives to a component.
No matter which route is visited, I need to use some data from an api.
This data is constant through the application. Using Redux I could dispatch an action FETCH_DATA in each React component and manage the logic of fetching in Redux actions.
However, this looks repetitive to me because I would be writing the same logic through all the React components that need data: look for the the data in the store. If it's there take it. If it's not there, dispatch FETCH_DATA.
What is another approach?
You can call that api in the App.tsx i.e. your first component to be rendered in the application. This is the component which actually holds the Routes also.
Consider the following code as an example and change it according to the need of your application:
class App extends React.Component {
ComponentDidMount() {
this.props.callTheApiHere()
}
render() {
return (
<Routes history={this.props.history} />
)
}
}
export default App;
Now each component that any of your Route render have to just get the data from the redux store using mapStateToProps in react-redux Connect
GraphQL offers an elegant solution for this requirement of sharing data across components belonging to different routes.
More information:
https://www.youtube.com/watch?v=p_kqcGW1jkY
Apollo GraphQL (React) Data sharing across components
I am trying to get information about the current route on a non-Route component.
React Router provides singleton versions of history (browserHistory
and hashHistory) that you can import and use from anywhere in your
application.
Previously, it would seem you could use browserHistory, but that no longer appears to be supported. I'm using react-router-redux^4.0.8 and #types\react-router-redux^5.0.1.
How can I access the current route location?
This is very simple. You can use the withRouter HOC
You can get access to the history object's properties and the closest
Route's match via the withRouter higher-order component. withRouter
will pass updated match, location, and history props to the wrapped
component whenever it renders.
It is used liked this:
import {withRouter} from "react-router-dom"
#withRouter
class NonRouteComponent extends Component {
render() {
// you have access to this.props.match
// you have access to this.props.history
// you have access to this.props.location
const { match, location, history } = this.props
return <h1>Hi {location.pathname}</h1>;
}
}
If you don't use decorators aka #withRouter you can export the component like so:
class NonRouteComponent extends Component { ... }
export default withRouter(NonRouteComponent);
First things first is this possible to have 2 components on a page one displaying simple static markup but the second one in my parent is going to be a div that displays a link for each item in an array and if you click on it at the bottom of the div then data will be displayed for each one?
If this is possible are dynamic props as simple as performing .map inside the element and printing out a route?
Will also add that I am receiving the following on my Router object but I installed react-router globally though it is also in my node_modules folder.
'react-router' does not contain an export named 'hashHistory'.
import React, { Component } from 'react';
import { Router, Route, IndexRoute, hashHistory } from "react-router";
class DetailsComponent extends Component {
render() {
return (
<Router history={hashHistory}>
// in here will map this.props.data and for each one print a route
// the component for that route will be a DetailedViewComponent that
// that takes in the data in the props and renders it
// so i might need a link? in this render method aswell?
</Router>
);
}
}
export default DetailsComponent ;
React Router match params can be used to render content dynamically
ie. <Route path="/dynamicroute/:id" component={DynamicComponent}/> will provide an id param that can be retrieved via props.match.params inside <DynamicComponent/>
Spoke to some more senior react developers and they said what I was after was npm package Component-Router I haven't got back onto the project just yet but will post full code once completed :)