How to use Route in ReactJS - reactjs

I need some guidance on the reactjs Routes and how to manifest them properly.
So I have my react directory as below (I will omit unnecessary directories and files)
Routes.js
App.js
src/
ComponentOne.jsx
ComponentTwo.jsx
My curent Routes.js looks like this
import ComponentOne from './src/ComponentOne'
import ComponentTwo from './src/ComponentTwo'
<Route path='/banuka/' exact component={ComponentOne} />
<Route path='/banuka/test' exact component={ComponentTwo} />
And in the ComponentOne.jsx there is a nothing but a button wrapped in a Link of react-router-dom
let valueobject = {<some-key-value-pairs>} // take a note on this line
<Link to={`/banuka/test`}>
<Button variant="dark">
Upload Data
</Button>
</Link>
So when I click on this Button it navigates me two ComponentTwo component.
This is where the problem comes in:
I want to pass the variable valueobject which is an object conains key-value pairs as props from ComponentOne to ComponentTwo
But as you can see, the ComponentTwo is already being rendered in the Routes.js
How can I achieve this?
You can suggest me a better way (even if it means, I have to delete the Routes.js, but it is better if I can keep it as is) to achieve this, so it will look like:
<ComponentTwo values={valueobject}/>
Thank you!

You can send as route state
https://reactrouter.com/web/api/Link/to-object
let valueobject = {<some-key-value-pairs>} // take a note on this line
<Link
to={{
pathname: '/banuka/test',
state: {
...valueobject,
},
}}
>
<Button variant="dark">
Upload Data
</Button>
</Link>

Related

React Router history on browser's back button

I have a home component with a link which loads a display component and in display component i have the same link which loads the display component again.
if user clicks the link many times in Display Component then there will be a lot of router history.i want that when a user clicks the browser back button it should load the home component not all the previous history.
when i use history.replace("/"); in Display component with onClick event then it works only one time back.but again back is resulting the previous history of Display Component
Routes.js
import Home from "./Components/Home"
import Display from "./Components/Display"
<Router>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/:city">
<Display />
</Route>
</Switch>
</Router>
Home.js
<Link to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>
Display.js
<Link to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>
Depending on the version of react router you are using you can just add the replace prop on your Link component in the Display.js file to not push new states on the history stack and instead update the current one.
<Link replace to ={`/${city}`} onClick={()=>{dispatch(fetchWeather(city)); }}>Search</Link>
If you're on an older version where this isn't supported what you can do is have a click handler do this for you
// Display.js file
function Display(props) {
// whatever code you have for this file
const handleViewCity = useCallback((event, city) => {
// prevent the default redirect from happening, were going to manage that ourselves
event.preventDefault()
dispatch(fetchWeather(city))
props.history.replace(`/${city}`)
}, [props.history, fetchWeather])
return (
// your jsx
// keep the href so you get browser builtin functionality like right click "open in new window"
<a href={`/${city}`} onClick={(e) => handleViewCity(e, city)}>Search</Link>
)
}
export default withRouter(Display)
To visualize what would be happening here think of history as a stack of locations. (this is a simple example - pseudo code)
history.push('/city1') // ['/home', '/city1']
history.push('/city2') // ['/home', '/city1', '/city2']
history.push('/city3') // ['/home', '/city1', '/city2', '/city3']
Pressing the browser back button fires a window popstate event. Pop being the keyword there. When the browser back button is pressed your history then looks like this ['/home', '/city1', '/city2'], which is why you were seeing different cities from the history.
Instead you want to use replace to achieve the desired effect
history.replace('/city1') // ['/home', '/city1']
history.replace('/city2') // ['/home', '/city2']
history.replace('/city3') // ['/home', '/city3']

Route path="/subject/:selectedSubject" does not change subject when navigated via < Link />

I'm trying to display articles filtered by subject. When you access directly path="/subject/:selectedSubject" (subject/TECH) for example it works perfectly. However if you navigate through <Link to={"/subject/TECH"} /> it will change the URL but will not load new articles.
I've tried: connecting everything with "withRouter".
I know that the <Link/> is changing the redux state, however that is not calling componentWillMount() which is where fetchArticles() is.
So, my question is, where should I put fetchArticles in order for it to be called when is triggered. I tried putting it inside render() but it keeps getting called non-stop. Or maybe I'm approaching this the wrong way.
PS: if another path gets called via <Link/>, like for example path="/", it works as intended (loads up new articles).
at App/
<BrowserRouter>
<div>
<Route path="/" exact component={ArticlesList} />
<Route path="/subject/:selectedSubject" component={ArticlesList} />
</div>
</BrowserRouter>
at ArticleList/
componentDidMount() {
if (this.props.match.params.selectedSubject) {
const selectedSubject = this.props.match.params.selectedSubject.toUpperCase();
this.props.fetchArticles(selectedSubject);
} else {
this.props.fetchArticles();
}
}
at Link/
{this.props.subjects.map(subject => {
return (
<Link to={`/subject/${subject.name}`} key={subject.name}>
<li>
<span>{subject.name}</span>
</li>
</Link>
);
})}
Since you use the same Component for / and /subject/:selectedSubject, he's not calling mounting lifecycle methods, including constructor() and componentDidMount().
You have to use updating lifecycle method, especially componentDidUpdate() to handle a update of your component.

Link changing URL but not the page

I am using Ant design breadcrumbs. I am trying to change the page using link and pushing the URL, I can see the URL change but the page in not changing.
I tried using Link, then creating a function for onClick but everything just change the URL.
Route:
<Route exact path="/assistant/:wId/skill/xyz/:sid" component={ xyz } />
Tried process 1:
<Breadcrumb separator=">">
<Breadcrumb.Item
onClick={this.redirectToParam2}>
{param2}
</Breadcrumb.Item>
</Breadcrumb>
redirectToParam2 = () => {
this.props.history.push(`/assistant/${wId}/skill/xyz/${sId}`);
}
Tried process 2:
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to= {`/assistant/${wId}/skill/xyz/${sId}`}>
{param2}
</Link>
</Breadcrumb.Item>
</Breadcrumb>
Even I tried without the Breadcrumbs component but it's still not changing the page.
I want the page to change as soon as the URL changes.
Thank you in advance.
Try this,
import { Link } from "react-router-dom";
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to= {`/assistant/${wId}/skill/xyz/${sId}`}>
{param2}
</Link>
</Breadcrumb.Item>
</Breadcrumb>
The problem you are running into, is that with changing the parameters used as props for the xyz component, the component is not replaced but gets new properties. Since nothing changes, i'm assuming you have state that gets filled either in the constructor or ComponentWillMount/ComponentDidMount.
React class components have a lifecycle function for this: componentDidUpdate.
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
Quote from react docs, See: https://reactjs.org/docs/react-component.html#componentdidupdate
componentDidUpdate(prevProps) {
if ((this.props.params.match.sid !== prevProps.params.match.sid) ||
(this.props.params.match.wid !== prevProps.params.match.wid)) {
this.populateState(this.props.params.match); //fill your state
}
}

Using React Router with different tag

React Router has a Link component with generates an HTML <a> element.
I need to generate an <area href=... >.
How can I do it?
You can have Link as child to your component.
<area>
<Link
to={'/path/to/somehwhere}'}>
Click Me
</Link>
</area>
Or you can specify a onclick event handler on area and use hashHistory or browserHistory from react-router to manually navigate user to desired location.
<area onClick={handleClick}>
// your code here
</area>
and handleClick will have
handleClick = (): void => {
hashHistory.push('/path/to/somehwhere'); // or browserHistory
}
In these both ways you can achieve your desired behavior. You can read more about navigating users programmatically here.
Edit: I forgot to mention that you can also wrap your whole component inside Link.
<Link to='/path/to/somewhere'>
<area />
</Link>

Set state property from URL using react-router

I have a container component with a modal in it that is opened and closed based on a state property.
I want to control this via the URL, i.e. I want to have
/projects - the modal is NOT open
/projects/add - the modal IS open
As well as being able to link directly to it, I want the URL to change when I click on links within the main container to open the modal.
Can someone explain how I could do this, or point me in the right direction of a good tutorial?
NOTE: This way is not perfect. Even more it's rather antipattern than pattern. The reason I publish it here is it works fine for me and I like the way I can add modals (I can add modal to any page and in general their components don't depends on the other app in any way. And I keep nice url's instead of ugly ?modal=login-form). But be ready to get problems before you find everything working. Think twice!
Let's consider you want following url's:
/users to show <Users /> component
/users/login to show <Users /> component and <Login /> modal over it
You want Login to not depend on Users in anyway, say adding login modal to other pages without pain.
Let's consider you have kinda root component which stands on top of other components. For example Users render may look something like this:
render() {
return (
<Layout>
<UsersList />
</Layout>
);
}
And Layout's render may look something like this:
render() {
return (
<div className="page-wrapper">
<Header />
<Content>
{this.props.children}
</Content>
<Footer />
</div>
);
}
The trick is to force modal's injection to <Layout /> every time we need it.
The most simple approach is to use flux for it. I'm using redux and have ui reducer for such page meta-information, you can create ui store if you use other flux implementation. Anyway, final goal is to render modal if <Layout />'s state (or even better props) contains modal equal to some string representing modal name. Something like:
render() {
return (
<div className="page-wrapper">
<Header />
<Content>
{this.props.children}
</Content>
{this.props.modal ?
<Modal key={this.props.modal} /> :
null
}
<Footer />
</div>
);
}
<Modal /> returns modal component depends on given key (In case of our login-form key we want to receive <Login /> component).
Okay, let's go to router. Consider following code snippet.
const modal = (key) => {
return class extends React.Component {
static displayName = "ModalWrapper";
componentWillMount() {
// this is redux code that adds modal to ui store. Replace it with your's flux
store.dispatch(uiActions.setModal(key));
}
componentWillUnmount() {
store.dispatch(uiActions.unsetModal());
}
render() {
return (
<div className="Next">{this.props.children}</div>
);
}
}
};
...
<Route path="users" component={Next}>
<IndexRoute component={Users}>
<Route path="login" component={modal('login-form')}>
<IndexRoute component={Users} />
</Route>
</Route>
(Don't care about Next - I add it here for simplicity. Imagine it just renders this.props.children)
modal() function returns react component that triggers change in ui store. So as soon as router gets /users/login it adds login-form to ui store, <Layout /> get it as prop (or state) and renders <Modal /> which renders corresponding for given key modal.
To programmatically assess to a new URL, pass the router to your component and use push. push for example will be call in the callback trigger by the user action.
When setting your router set a route to /projects/:status. then, in your component route, you can read the value of status using this.props.param.status. Read "whats-it-look-lik" from react-router for an example.

Resources