React-Router: pass props from one component to another using Navlink - reactjs

I am using <NavLink> for routing now how can i pass props into <NavLink> to another Component

The way you can pass data to navigated component using Link and NavLink
<Link to={{
pathname:'/home',
state: {name:'from home page'}
}}>
Go to Home
</Link>
or using NavLink
<NavLink to={{
pathname:'/home',
state: {title:'from home page'}
}}>
Go to Home
</NavLink>
inside navigated component you can access like this.
if it's hooks
import {useLocation} from 'react-router-dom'
function App(){
let location = useLocation();
console.log(location)
return <h2>...</h2>
}
if it's class based component
console.log(this.props.location)

On the first component do this
<NavLink
to={{
pathname:"/priview-data",
aboutProps:{
selectedidds:this.state.selectedIds
}
}}
exact
>Preview Question
</NavLink>
and now on another component
constructor(props) {
super(props);
console.log(props.location.aboutProps);
}
hope it helps
Thanks

You can also use hooks to pass props:
<Link
to={{
pathname: "/home",
userProps: {name: "This is a passed prop"},
}}>
Go to home
</Link>
To access it from another component
import { useLocation} from "react-router-dom"
function OtherComponent() {
let location = useLocation();
console.log(location.userProps);
}

<NavLink to="/onboarding/profile" state={{ from: "occupation" }}>
Next Step
</NavLink>
Now the only question that remains is, how do we get access to the data on state so we can update the UI based on it? To answer that, we need to look at the component that's being rendered at the /onboarding/profile route. For our example, let's call it Profile.
import { useLocation } from 'react-router-dom'
function Profile () {
let location = useLocation();
console.log(location.state.from);
return (
...
)
}

Related

React couldn't pass a data from one class component into another using Link

I am trying the pass data to a new page by using Link, in doing so I used the following code.
<Link
className="option"
to={{
pathname: this.state.pathname,
state: id
}}
>
<span className="color-primary"> <button style={{ color: "white" }}
className="transaction-button"><i className="material-icons" style={{ fontSize: "18px" }}>sync_alt</i> Transaction</button>
</span>
</Link>
In the page routed, I tried to handle the data by the following code.
console.log(this.props)
The output is an empty object.
{}
Both pages are class component
I assume you are using react-router.
In the first page, where you use <Link>...</Link> you're doing the right thing.
At this point there are two alternatives: you can use function or class to create the component.
IF YOU USE A FUNCTION
In the second page, to take the data you passed, you have to import useLocation:
import { useLocation } from 'react-router';
And then, inside the function, you have to call it and extract the state from it:
const location = useLocation();
console.log(location.state);
Inside location.state you have the state you passed from the previous page.
IF YOU USE A CLASS
In this case, things are little more complicated, but you can use withRouter in order to inject location inside your component props.
So, first of all you need to import PropsTypes and withRouter:
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
Then you have to write your class like this:
class Child extends React.Component {
static propTypes = {
location: PropTypes.object.isRequired,
};
render() {
const { location } = this.props;
console.log(location.state);
return {
<div> ... </div>
};
}
}
export withRouter(Child);
In this way inside location.state you have the state you passed from the previous page.
If you are using a class, there is no withRouter any more.
What happened to withRouter? I need it!
This question usually stems from the fact that you're using React class components, which don't support hooks. In React Router v6, we fully embraced hooks and use them to share all the router's internal state. But that doesn't mean you can't use the router. Assuming you can actually use hooks (you're on React 16.8+), you just need a wrapper.
So, you'll need to create your own wrapper, as shown in the docs.
Here is my implementation, a little more easy-to-use than doc example:
import React from 'react';
import {useLocation, useNavigate, useParams} from 'react-router-dom';
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...props} {...{location, navigate, params}} />;
}
return ComponentWithRouterProp;
}
export default withRouter;
Use (when exporting your component):
export default withRouter(Link);
More Use-cases Example -> for other people that came here:
Example of loading batch of components wrapped with withRouter, or just your Link component.
const routingList = [{title: 'Home', search: '/', component: Home, icon: 'fa-home'},{...}]
<Routes>
{
routingList.map((routing) => {
let Child = routing.component;
return <Route key={routing.search} path={routing.search} element={<Child {...routing.compProps} />} />;
})
}
<Route path="/link" element={<Link />} />
</Routes>
And, in your component, you can use:
this.props.location.pathname
this.props.params.paramName

How do I redirect to an External Link in react?

I am building a gallery where you click on the image and it will load in a separate component using props, this image is a URL, taken from an array, where the src property is loaded as a background image via CSS. My challenge is connecting the src data to the child component. See original question
I have found a solution to pass the data using the Link component. Now the URL string is being read like this: http://localhost:3000/https://photos.smugmug.com/photos....
As you can see there is an address within the address in the string.
I have tried changing the state of the URL string but did not work.
My question, how do I write a redirect to fix the HTTP address removing the localhost address
UPDATE
Many thanks to Taylor, Drew, and Ajeet for all of your help!
The solution is posted below, the main issue was I needed a function in the Image component to connect the src props from the GalleryContainer component.
I also changed all "a tags" to "Link components" to keep consistency. More details are in the explained solutions from Drew and Taylor, and also Ajeet code box here
Issues
I don't know why but you don't seem to use Link components consistently in your app; when using anchor (<a>) tags these types of links will reload the page and your app. A similar issue occurs when you manually set the window.location.href.
The Image wasn't correctly accessing the passed route state.
Solution
App
Reorder your routes from more specific to least specific, and remove the link from within the Switch component, only Route and Redirect components are valid children.
function App(props) {
return (
<>
<Router>
<Switch>
<Route path="/gallery" component={GalleryList} />
<Route path="/image" component={Image} />
<Route path="/" component={Home} />
</Switch>
</Router>
</>
);
}
Home
Use Link component to enter the gallery.
import { Link } from "react-router-dom";
...
<Link to="/gallery">
<h4>Click Here to Enter Gallery!</h4>
</Link>
GallerayList
Use Link component for the link back home.
import { Link } from "react-router-dom";
...
<Link to="/">Home</Link>
GalleryContainer
Refer to image source consistently, i.e. src. Pass along also the image id in route state, using a Link.
const GalleryConatiner = (props) => {
return (
// generates the gallery list!
<ul>
<li className={styles["gallery-list"]}>
<Link
to={{ pathname: "/image", state: { id: props.id, src: props.src } }}
>
<div
className={styles["div-gallery"]}
style={{
backgroundImage: `url(${props.src})`,
height: 250,
backgroundSize: "cover"
}}
></div>
</Link>
</li>
</ul>
);
};
src/Public/Image
Use a Link for the link back to the gallery. Use the useLocation hook to access the passed route state.
import { Link, useLocation } from 'react-router-dom';
const Image = (props) => {
const { state: { id, src } = {} } = useLocation();
return (
<section>
<h1 className={styles["h1-wrapper"]}>Image :{id}</h1>
<div className={styles.wrapper}>
<Link to="/gallery">BACK TO GALLERY</Link>
<ImageContainer id={id} key={id} src={src} />
</div>
</section>
);
};
src/Public/ImageContainer
It isn't clear what your plan is for this component and clicking on the div rendering the passed image as a background so just remove the window.location.href logic with history.push if you want to navigate elsewhere. You can access the history object via the useHistory React hook.
Demo
The disconnect is between the GalleryContainer and Image components. In order to access data from the <Link to=...> within the next component, you need to use props.location.propertyName.
So for example, your GalleryContainer needs to link like this:
<Link to={{ pathname: "/image", src: props.src }}>
And then the value can be retrieved inside the Image component like so:
<ImageContainer id={props.id} key={props.id} src={props.location.src} />
You can use
<Link to={{ pathname: "/image", state: { url: props.src } }}>
but then you would have to access it in the linked component like this: props.location.state.url
From there, you can use an <a> tag with an href to link to the src property.
You can simply use a tag to redirect.
<a target='_blank' href={}>
Link
</a>
Remove target attribute if you dont need to open in new tab.

can't use child props in react

I have a problem with the code - I do not understand how to solve it - I tried for a few hours - it does not work.
I do not know what's going on. I'm just trying to access from a component of courses - and a component of a single course.
I get this error:
This is the code I wrote down so far - not a long code.
course.js:
import React, { Component } from 'react';
class Course extends Component {
render() {
return (
<div>
<h1>this.props.location.title</h1>
<p>You selected the Course with ID: {this.props.match.params.id}</p>
</div>
);
}
}
export default Course;
courses.js:
import React, { Component } from 'react';
import './Courses.css';
import { Link } from "react-router-dom";
class Courses extends Component {
state = {
courses: [
{ id: 1, title: 'Angular - The Complete Guide' },
{ id: 2, title: 'Vue - The Complete Guide' },
{ id: 3, title: 'PWA - The Complete Guide' }
]
}
render() {
return (
<div>
<h1>Amazing Udemy Courses</h1>
<section className="Courses">
{
this.state.courses.map(course => {
return (
<Link
key={course.id}
to={{
pathname: this.props.match.url + "/" + course.id,
title: course.title
}}>
<article className="Course">{course.title}</article>
</Link>
)
})
}
</section>
</div>
);
}
}
export default Courses;
app.js:
import React, { Component } from 'react';
import Courses from './containers/Courses/Courses';
import Users from './containers/Users/Users';
import { NavLink, BrowserRouter, Switch, Route } from "react-router-dom";
import './App.css';
class App extends Component {
render() {
return (
<BrowserRouter>
<div className="App">
<header>
<nav>
<ul>
<li>
<NavLink
to="/users"
exact
activeClassName="my-active"
activeStyle={{
color: '#fa923f',
textDecoration: 'underline'
}}>Users
</NavLink>
</li>
<li>
<NavLink to={{
pathname: '/courses',
hash: '#submit',
search: '?quick-submit=true'
}}>Courses
</NavLink>
</li>
</ul>
</nav>
</header>
<Switch>
<Route exact path="/">
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/courses">
<Courses />
</Route>
</Switch>
<ol style={{ textAlign: 'left' }}>
<li>Pass the course ID to the "Course" page and output it there</li>
<li>Pass the course title to the "Course" page - pass it as a param or score bonus points by passing it as query params (you need to manually parse them though!)</li>
<li>Load the "Course" component as a nested component of "Courses"</li>
<li>Add a 404 error page and render it for any unknown routes</li>
<li>Redirect requests to /all-courses to /courses (=> Your "Courses" page)</li>
</ol>
</div>
</BrowserRouter>
);
}
}
export default App;
You're trying to access the props that come from react router, with the syntax that you're using on your app.js file you're not passing any props. To be able to pass react router props to your component you can use this syntax instead:
<Route path="/courses" component={Courses} />
So everytime you access a link that takes you to the courses you will have available in you component all the props that are coming from react router.
Here's a sandbox with an example of a component that uses that syntax to obtain all the router props vs another that uses the syntax you're using and it does not have access to the router props:
Sandbox with example
I'm using functional components but with your example doing this.props will give the router props including the match object. With your courses component being a class I think this is the easiest way to do it, you can also wrap the component in a withRouter HOC to accomplish the same thing:
WIth router docs
You need to set props in your child components for them to be available, like so...
<Courses
match={this.props.match}
/>
Right now, all you have is <Courses />, without passing an props. So in the Courses class, of course you'll get this message: Cannot read property 'url' of undefined on your call of <Link ....this.props.match.....>.
But if you pass along the match prop, like match={this.props.match}, there should no longer be this error.
Take a look at the ReactJS documentation on how props work...
However, elements can also represent user-defined components:
const element = <Welcome name="Sara" />;
When React sees an element representing a user-defined component, it passes JSX attributes and children to this component as a single object. We call this object “props”.
Source: ReactJS.org: Components and Props

Can I add a property or state into Next JS Router like React-Route?

I would like to know if it's possible to add a state inside a Router.router link using Next.JS.
I know how to handle router.query but i don't want to show the state on that query.
You can try
<Link
to={{
pathname: "/courses",
search: "?sort=name",
hash: "#the-hash",
state: { fromDashboard: true }
}}
/>
later you will be able to access that state in the location object.
see:
https://reacttraining.com/react-router/web/api/location
https://reacttraining.com/react-router/web/api/Link
Edit: with Next JS's Link component it is a bit more limited. you could utilize the as prop.
<Link href="/dashboard?from=loginPage" as="/dashboard" />
only /dashboard will be shown in the URL, you will still have access to from=loginPage in the query. You will have to serialize your state to a query string (and values will become string.
here's a quick example:
in Nav:
<Link href='/?from=navClick' as="/">
<a>Home</a>
</Link>
In the home component (which renders on '/')
import { useRouter } from 'next/router';
const Home = () => {
const { query } = useRouter();
return (
<>
{query.from ? `You are from: ${query.from}` : 'Well met.'}
</>
);
};
If the user goes to the home page by clicking on that link, you will get the from value in the query object, however it won't be rendered in the browser's address bar.

Why doesn't <Link to=...> work, but push() works on production environment?

In my React app, I'm using two ways to redirect urls.
One is component, which is something like
import { Link } from "react-router-dom";
<Link to={{ pathname: "/search" }}>
<img
src={image.image}
alt="product"
className="slick-image"
/>
</Link>
The other one is push() from Redux, which is like
import { push } from "react-router-redux";
push(`search/${searchTerm}`)
They both are working very well in local environment, but in production environment, <Link> doesn't work at all, while push() works very well.
Does anyone have any idea why is that?
i think you need to wrap your component with withRouter that is usiing Link.
import { Link } from "react-router-dom";
class SomeComponent extends React.Component {
...
...
render () {
<div>
<Link to={{ pathname: "/search" }}>
<img
src={image.image}
alt="product"
className="slick-image"
/>
</Link>
</div>
}
}
export default withRouter(SomeComponent);

Resources