Navigate to other page with props in React JS - reactjs

I want to redirect from one page to other and pass props along with it. But i dont want these params in url.
Method :
saveForLater(){
if (typeof window !== "undefined") {
window.location.href = "./DataDisplay/";
}
};
I checked with urlparams we can set {"id":content} in url. But i do not wish to pass data in url.
I can not use Link / Route in the method . Is there any way to do it/any library to checkout? Pls suggest
CODE SAMPLE:
import React, { Component } from "react";
class DATAFETCH extends Component {
constructor(props) {
super(props);
this.state = {
Attachments: [],
validated: false,
isDoctor:false,
}
saveForLater(){
if (typeof window !== "undefined") {
window.location.href = "./DataDisplay/";
}
};
render() {
return (
/////// Various Fields
<Button
onClick={() => props.submit()}
>
)}

I think you've not quite understood how react works. Both Link and Route are components, so they can only be used & rendered within the render lifecycle function of a class-based component or return of a functional component. Outside of this the pattern to navigate to other routes is to use the history object to push to another route/path.
history.push('/DataDisplay');
If you need to send extraneous data along with the route push, you can use route state.
history.push({
pathname: '/DataDisplay',
state: {
// any values/objects/etc you want to also send to pushed route/path
},
});
Route state is accessed via the location object of route-props.
const { /* values/objects/etc */ } = props.location.state;
The route-props are only accessible via a component directly rendered by a Route, or by using the withRouter Higher Order Component or in the case of functional components using react hooks.
Your example code
import React, { Component } from "react";
import { withRouter } from 'react-router-dom'; // <-- import withRouter HOC
class DATAFETCH extends Component {
...
saveForLater(){
history.push({
pathname: '/DataDisplay',
state: {
id: content // <-- pass in content to key `id`
},
});
};
render() {
...
}
}
export default withRouter(DATAFETCH); // <-- decorate with withRouter HOC
To access on resultant route's component
props.location.state.id

Maybe you can use a store manager like the API Context.
Or one alternative like MobX or Redux.

Related

How to render a component with props derivative from NextJS router

I'm trying to render a component that uses a dynamic router path prop. I want mysite.com/something to load the component with the something prop. If the route is mysite.com/somethingelse, I want to load the component with the somethingelse prop. Here's my code:
page.js:
import { useRouter } from "next/router";
import List from "./List";
function DefaultPage() {
const router = useRouter();
console.log(router.query.category); // Works correctly
return (
<div>
<List category={router.query.category} />
</div>
);
}
export default DefaultPage;
The component, list.js:
import React, { Component } from "react";
class List extends Component {
constructor(props) {
super(props);
console.log(this.props.category); // This is where I'm confused
}
static defaultProps = { category: "default" };
render() {
return <p>Hello</p>;
}
}
export default List;
The problem is, this.props.category always returns as default (my default prop), unless I recompile. It works perfectly after a fresh compile, but then breaks after every subsequent refresh in the browser.
I can visually see the router query returning the correct value in the log, but the component is rendering before everything else, thus returning a default value. Is there a way I can stop the List component from rendering before its own props are specified? Or is there a better way of doing this all together? Thanks.
I would do something like this in the DefaultPage component:
if(router.query.category === 'something') {
return <ListComponent/>
}
if(router.query.category === 'somethingElse') {
return <SomethingElseComponent/>
}
If you don't want to use two separate components, you could pass the prop to useEffect so it can re-render the component when that prop changes https://reactjs.org/docs/hooks-effect.html

Share data between component in react

I have a component which is extending form like:
import React from "react";
import Form from "./common/form";
import AppFeature from "./common/appFeature";
class AddFeature extends Form {
render() {
<AppFeature role={"newRole"} />}
}
When I tried to get the role value in appfeature component through this.props.role. I'll get undefined.
Any workaround for this???
You're doSubmit function is not correct.
You need to update state - showFeatures to true to render AddFeature component.
You are calling history.push to a specific route which is not defined and uses React Router to do that.
Use below code and you will be able to get props to AddFeature component
doSubmit = async ex => {
try {
this.setState({
showFeatures: true
})
} catch (ex) {
console.log(ex.response);
}
};

Route authorization HOC cause to remount children 3-times

I'm using an HOC component to restrict access to the route for non-logged users. The problem that this HOC remount children components while mounting or re-rendering when access this route directly from url(on the app first load). For example I have a 3 times did mount in the PaperWorkProgress component.
Route definition:
<Route path="/paperwork/progress" component={RequireAuth(PaperWorkProgress)}/>
Here the HOC code:
import React, {Component} from 'react';
import {connect} from 'react-redux';
export default function(ComposedComponent) {
class Authentication extends Component {
// check if token exists in storage
componentWillMount() {
const token = localStorage.getItem('token');
if (!token) {
const {pathname, search} = this.props.location;
this.props.history.push({
pathname: '/signin',
search: `?redirect_to=${pathname}${search}`,
});
}
}
// additional check
componentWillUpdate(nextProps) {
if (!nextProps.loggedIn) {
const {pathname, search} = this.props.location;
this.props.history.push({
pathname: '/signin',
search: `?redirect_to=${pathname}${search}`,
});
}
}
render() {
return <ComposedComponent {...this.props} />;
}
}
function mapStateToProps(state) {
return {loggedIn: state.session.loggedIn};
}
return connect(mapStateToProps)(Authentication);
}
Any ideas?
This question may be from a while ago already, but I just encountered the same problem.
In the end I found out that my HOC function was actually called on every route change.
What helped for me was to create the authorized component only once on initialization:
const AuthorisedDashboard = requireLogin(Dashboard);
and then later just use it
<Route path="/dashboard" component={AuthorisedDashboard} />
Or, you know, I guess you could just export the component with the HOC function already applied if it is only ever used in authorised mode...
I'm not sure this will make a difference about the re-rendering problem, but your code feels wrong.
First, you seems to have 2 source of truth, your redux store and the localStorage, which complicates things. If you want to "hydrate" your store from previous navigation information, you should use the createStore "preloadedState" argument, not checking everytime in your component. Cf Redux doc and this video from the creator of Redux himself Video for persisting and rehydrating State. Once your state comes only from your store it starts to be more simple.
Second,
When you push to the history object inside the component, It feels like you are mutating the component own props (as history is a prop). That feels weird to me and could be the root of your problem.
Why not use the Redirect component inside your render method like this instead ? cf React router docs. The component will looks like this (obviously you would need to change your Login component too, like in the docs)
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
export default function(ComposedComponent) {
class Authentication extends Component {
render() {
return !this.props.loggedIn ? (
<Redirect
to={{
pathname: "/login",
state: { from: this.props.location }
}}
{...this.props}
/>
) : (
<ComposedComponent {...this.props} />
);
}
}
function mapStateToProps(state, ownProps) {
return { loggedIn: state.session.loggedIn, ...ownProps };
}
return connect(mapStateToProps)(Authentication);
}

React router, pass data when navigating programmatically?

We could navigate to different path using
this.props.router.push('/some/path')
Is there a way to send params (object) along when navigating?
There are other options I can think of, but wonder if passing object is possible at all?
I could embed id of the object and refetch the object from server
from the new page.
Or I could store the object in global storage like redux store. (This object needs to be removed from the store soon. So I'm thinking it might not be good to put it there in the first place)
The current answers are outdated.
React Router 6:
Use the useNavigate hook:
const navigate = useNavigate();
navigate('/other-page', { state: { id: 7, color: 'green' } });
Then, you can access the state data in '/other-page' via the useLocation hook:
const {state} = useLocation();
const { id, color } = state; // Read values passed on state
React Router 4 or 5:
Call history.push, and pass an object as the 2nd param to pass state:
props.history.push('/other-page', { id: 7, color: 'green' }))
Then, you can access the state data in '/other-page' via:
props.location.state
React Router uses location objects. One of the properties of a location object is state.
this.props.router.push({
pathname: '/other-page',
state: {
id: 7,
color: 'green'
}
})
On the page that is navigated to, the current location will be injected into the component whose route matched, so you can access the state using this.props.location.state.
One thing to keep in mind is that there will be no state if a user navigates directly to the page, so you will still need some mechanism to load the data when it does not exist.
Best way to pass data to the target Component, just copy paste the code and see the magic, I also explained it in depth.
Remember: in react-router-dom v6 you can use hooks instead.
version 5.X
Let's suppose we have two Components first and second. The first has the link which will target the second component.
The first Component where the link is, by clicking the link you will go to the target path as in my case it is:"/details".
import React from 'react';
import {Link} from 'react-router-dom';
export default function firstComponent() {
return(
<>
<Link to={{
pathname: '/details',
state: {id: 1, name: 'sabaoon', shirt: 'green'}
}} >Learn More</Link>
</>
)
}
Now in the second Component you can access the passed object as:
import React from 'react'
export default class Detials extends React.Component{
constructor(props){
super(props);
this.state={
value:this.props.location.state,
}
}
alertMessage(){
console.log(this.props.location.state.id);
}
render(){
return (
<>
{/* the below is the id we are accessing */}
hay! I am detail no {this.props.location.state.id} and my name is
{this.props.location.state.name}
<br/>
<br/>
{/* press me to see the log in your browser console */}
<button onClick={()=>{this.alertMessage()}}>click me to see log</button>
</>
)
}
}
note:In version 6 of react-router-dom the above method won't work on class components though you can use functional components of react by using useLocation hook and then you can draw the state object through that location in another component.
version 6
How to achieve the same using hooks v6 of react-router-dom
Let's suppose we have two functional components, first component A, second component B. The component A wants to share data to component B.
usage of hooks: (useLocation,useNavigate)
import {Link, useNavigate} from 'react-router-dom';
function ComponentA(props) {
const navigate = useNavigate();
const toComponentB=()=>{
navigate('/componentB',{state:{id:1,name:'sabaoon'}});
}
return (
<>
<div> <a onClick={()=>{toComponentB()}}>Component B<a/></div>
</>
);
}
export default ComponentA;
Now we will get the data in Component B.
import {useLocation} from 'react-router-dom';
function ComponentB() {
const location = useLocation();
return (
<>
<div>{location.state.name}</div>
</>
)
}
export default ComponentB;
For functional component and react-router-dom:^5.2.0 let's take a very simple example to make the concept precise
import { useHistory } from "react-router-dom";
function Sender(){
const history = useHistory();
const goToReceiver = () => {
history.push("/receiver", { name:'Mr',age:23 });
}
return <button onClick={goToReceiver}>Go To Receiver</button>
}
Now lets see how tha data came to receiver route
import { useLocation } from "react-router-dom";
function Receiver(){
const location = useLocation();
return <div>
<p>{location.state.name}</p>
<p>{location.state.age}</p>
</div>
}
You could make a use of useHistory hook of react-router-dom.
Below code is for you to pass your data to the stated route which is "/dashboard".
let history = useHistory();
history.push({
pathname: '/dashboard',
state:{
tags: 'your-value'
}
});
and from the "/dashboard " you can use the useHistory() to receive the above data.
Below code is for you to receive your data.
const Dashboard =()=>{
let {location} = useHistory();
return (<>{location.state.tags}</>)
}
Passing query parameters when programatically navigation in react router
History objects may be used programmatically change the current location using both history.push and history.replace.
history.push('/home?the=query', { some: 'state' })
If we pass the history object down into a component as props. Then we can navigate programatically using the react router methods available on the history object.
Now lets assume you are passing down the history object as a prop called 'router'. So it would be referenced inside a component with class based syntax like:
this.props.router
When using push or replace you can either specify both the URL path and state as separate arguments or include everything in a single location-like object as the first argument.
this.props.router.push('/some/path?the=query')
Or you can use a single location-like object to specify both the URL and state. This is equivalent to the example above.
this.props.router.push({
pathname: '/some/path', //path
search: '?the=query' // query param named 'search'
})
Note - Of course make sure that the this.props.router is actually the history object from the react-router api.
Their are use cases for sharing data using react-router dom
use react-router-dom useNavigate();
than =>
const navigate = useNavigate();
navigate('/toPath', {state: customData})
in other component lets say you want to fetch that data
use another hook that is useLocation()
const location = useLocation()
location.state // data will be shared by navigate
I was not able to get this working with react-router v4+. But the following does work:
//I.e. add navigate using the history stack and pass context as the 2nd parameter
this.props.history.push('/takeTest', {
subjectsList: this.props.subjectsList.filter(f => f.isChecked === true)
})
For sending:
navigate("/success", {
state: {
stripeData: res.data,
products: cart,
},
});
For Receiving:
import { useLocation } from "react-router-dom";
const location = useLocation();
const data = location.state.stripeData;
const cart = location.state.products;
if you want to send it in query string
this.props.router.push({
pathname: '/payment-history',
query: {
email: rowData.email
}
})

how to rerender a component when hash change and change the state

I want to monitor hash change and then change the state and rerender the component. so I want to know where to monitor the hash change in component lifecycle
example:
#/detail/:id => #/detail
{info:[a:1,b:2]} => {info:[]}
.#/detail/:id and #/detail are the same components
If you want your component to have event listeners, you want to add those event listeners in componentDidMount, and remove the event listeners in componentWillUmount.
componentDidMount() {
window.addEventListener("hashchange", this.doSomething, false);
}
componentWillUnmount() {
window.removeEventListener("hashchange", this.doSomething, false);
}
Use HashRouter in index.js and withRouter to get path parameters and getDerivedStateFromProps to handle the states based on the url's hash.
index.js
import ReactDOM from 'react-dom';
import { HashRouter } from "react-router-dom";
import App from './App';
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>,
document.getElementById('root')
);
App.js
import { withRouter } from 'react-router-dom';
class App extends Component {
state = { productId: null };
static getDerivedStateFromProps(nextProps){
const { location: { pathname } } = nextProps; // pathname e.g.: '/detail/:8'
const productId = pathname.split(':')[1] || null; // ProductId: 8 OR null;
return { productId };
}
...
}
export default withRouter(App);
For React newer version
Use window.location.hash with useEffect
useEffect( ()=> {
doSomething();
}, [window.location.hash])
If you want to monitor the change in route params you may do that it the lifecycle method componentWillReceiveProps
componentWillReceiveProps
Use this as an opportunity to react to a prop transition before
render() is called by updating the state using this.setState(). The
old props can be accessed via this.props. Calling this.setState()
within this function will not trigger an additional render.
componentWillReceiveProps: function(nextProps) {
const {params: {id}} = nextProps;
if(id !== this.props.params.id){
/////////
}
}

Resources