Pass onclick function to component using react router - reactjs

I'm (obviously) fairly new to ReactJS. I'm using Router v6 for navigation between pages. I have a case where the action of a button on Component A must depend on which component (B or C or ...) launched the page. But, using useNavigate, it doesn't seem to be possible to send a function to the target component A, only text fields seem to be allowed in the object. That is, the following doesn't work:
let navigate = useNavigate()
function nextClick() {
navigate('/UserProfile', {state:{
okClick: userProfileOkClick,
cancelClick: backButtonClick
}})
}
That is, I want to navigate to the UserProfile component, and pass the callback functions its buttons should use. It doesn't work -- in the UserProfile page, the state object is null.
So is there a way to navigate to a new page and dynamically change the button callbacks? I hope so...
Thanks!

Related

Gatsby's `navigate` function to navigate to the same page?

Is it possible to use Gatsby's navigate function to navigate to the same page?
For instance, I built a search component that lives on the navigation bar, therefor accessible on any page. Once users perform search, they get routed to the Search Results page via navigate(/search-results).
If they are on that page, and want to perform the search again, that navigate function does not work, hence no results display.
I'm aware I could do a window.location.reload() but wanted to see if there are other ways.
Gatsby's navigation (Link component or navigate function) doesn't support parameters per se as you want to.
From Gatsby's docs:
Neither <Link> nor navigate can be used for in-route navigation with a
hash or query parameter. If you need this behavior, you should either
use an anchor tag or import the #reach/router package—which Gatsby
already depends upon—to make use of its navigate function, like so:
So, that said, you have two approaches:
Importing the navigation from #reach/router:
import { navigate } from '#reach/router';
onClick = () => {
navigate('?foo=bar');
}
Using window.location as you pointed
Of course, the #reach/router will give you a smoother workaround.

Conditionally render routes in createBottomTabNavigator react-navigation

In my react-native application I want to conditionally render routes in my navigation menu. I use react-navigations createBottomTabNavigator for my navigation menu.
The condition is based on a users role and is retrieved through an api-call.
The api-call does not happen on initial load-up.
I am using mobx-state-tree for application state management, the role is stored in a MST store.
I have to decorate the top-level container createAppContainer with observer expecting a re-render when the observed role is changing. It seems inefficient to re-render at top-level and I could not make it to work anyway.
It is not possible to pass down props down through the HOC provided by react-navigation as far as I am concerned. And I cannot wrap them in components for achieving this, since they are used as arguments for the HOC's.
For some structural clarification I am using the following HOC's
createAppContainer for wrapping my app. I pass it a
createSwitchNavigator and pass it a
createBottomTabNavigator that gets passed multiple
createStackNavigator that gets passed the actual screens. These are the ones that needs to be conditionally rendered.
//how can I at this level conditionally render
function screenExcluder() {
const stacks = {
SomeStackNavigator,
//...
}
console.log("MainTabNavigator, profilestore ", )
if (UserStore.isRegularUser){
stacks.ProfileStack = ProfileStack
stacks.SettingsStack = SettingsStack
}
return stacks
}
export default createBottomTabNavigator(
screenExcluder(), {
tabBarComponent: Tabs,
})
I expect a solution to make createBottomTabNavigator conditionally render routes based on values based on changing values in a mobx-state-tree store
I am using react-navigation version 3.0.9.
This plugin doesn't allow dynamic routes at the moment.
https://react-navigation.canny.io/feature-requests/p/dynamic-routes-for-navigators
You have to define all routes when the app is starting up. you can look for an alternative plugin like react-router
for more options join this thread
Suggession
or you can change the way you plan your route security like rendering all the routes and securing them based on user
more info: https://reactnavigation.org/docs/en/auth-flow.html

Using this.props.history.push("/path") is re-rendering and then returning

Edited the question after further debugging
I am having a strange issue, tried for a while to figure it out but I can't.
I have a React Component called NewGoal.jsx, after a user submits their new goal I attempt to reroute them to my "goals" page.
The problem: After they submit the browser loads in my goal page, but only for one second. It then continues and goes BACK to the NewGoal page!!
I am trying to understand why this is happening, I am beginning to feel that this might be an async issue.
Here is my code, currently it is using async-await, I also tried the same idea using a .then() but it also didn't work:
async handleSubmit(event)
{
const response = await axios.post("http://localhost:8080/addGoal",
{
goalID: null,
duration: this.state.days,
accomplishedDays: 0,
isPublic: this.state.isPublic,
description: this.state.name,
dateCreated: new Date().toISOString().substring(0,10),
}) */
// push to route
this.props.history.push("/goals");
}
While debugging, I tried taking out the functionality where I post the new message, and just did a history.push, code is below - and this completely worked.
// THIS WORKS
async handleSubmit(event)
{
// push to route
this.props.history.push("/goals");
}
But as soon as I add anything else to the function, whether before the history.push or after, it stops working.
Any advice would be very very appreciated!
Thank you
In the React Router doc's the developers talk about how the history object is mutable. Their recommendation is not to alter it directly.
https://reacttraining.com/react-router/web/api/history#history-history-is-mutable
Fortunately there are few ways to programmatically change the User's location while still working within the lifecycle events of React.
The easiest I've found is also the newest. React Router uses the React Context API to make the history object used by the router available to it's descendents. This will save you passing the history object down your component tree through props.
The only thing you need to do is make sure your AddNewGoalPage uses the history object from context instead of props.
handleSubmit(event)
...
//successful, redirect to all goals
if(res.data)
{
this.context.history.push("/goals")
}
...
})
}
I don't know if you're using a class component or a functional component for the AddNewGoalPage - but your handleSubmit method hints that it's a member of a Class, so the router's history object will be automatically available to you within your class through this.context.history.
If you are using a functional component, you'll need to make sure that the handleSubmit method is properly bound to the functional component otherwise the context the functional component parameter is given by React won't not be available to it.
Feel free to reply to me if this is the case.

How to use React Router to route to another url AND re-render page with NEW component?

In App.js, I have a button that if you click, should redirect users using React-Route to another URL, /landingpagehahaha, and should render a component called LandingPage. However, neither the URL is being changed in my browser nor the correct component being rendered. The behavior right now when you click the button is that the current page gets re-rendered, not the correct LandingPage component.
The React-Route logic is placed in a function called routeChange(). I put 2 alert() statements in it which get called, telling me that it is getting inside that function. However, nothing else changes.
I have tried using this.props.history.push("./LandingPage"); in routeChange() but it doesn't get past that statement. It appears like it behaves like response.json(), which returns from the function after it runs.
I have also tried using withRouter(), but I get a weird error that I can't call Route inside Router. I was unable to resolve that issue.
// Changes route
routeChange() {
alert("HELLO BEFORE");
alert("HELLo");
return (
<div>
<Route path="/landingpagehahaha" component={LandingPage} />;
</div>
);
}
// The button that is supposed to bring user to next page
<button onClick={this.routeChange}>Go To Next Page</button>
You need to return the Redirect component from your render function, or as use the history api to push the route into your navigation stack.
The first thing you should do, is move out the route declaration, and play it higher up in your component hierachy, you have to make sure the route declaration is rendered, when you're trying to go to the route.
Instead of using the history api, you could also use the Redirect component provided by react-router. I've made a small example here.
https://codesandbox.io/s/bold-paper-kxcri

Component not receiving props when pushed from same path - react router

This is the code that works ...for pushing to different routes
sendToMessage = (item)=>{
let path = '/messages';
this.props.history.push({
pathname:path,
state:{
clientid:item.clientid,
clientname:item.clientname
}
});
}
So, when I click on the item from a different route it works perfectly and the component receives the props sent
But when I am on the /messages page already and click on the item, I am not receiving the props so I can update the page with the new data.
why is this happening?
I'd like it to work for all situations,
whether from same route or from different route.
Now, I do receive this warning
Hash history cannot PUSH the same path; a new entry will not be added to the history stack
This is how I am trying to access the props in the destination component
componentWillReceiveProps(nextProps) {
console.log('nextPRops : ',nextProps);
}
but shouldnt I be able to still receive the props that I am sending??
is there any other way to send props to other component? these components are not parent and child component, so cant really pass props like that.
More details : it's a feature where a user gets a notification of message and gets taken to the messages page to that specific message he/she clicked on. Now, it may happen that the user is already on that page when the notification appears. in that case, we still need to show the new message when the user clicks on it. although it was made to bring to different route but it should also work when client is already on that page hope this makes it a little more clear
Method 1:
You can use redux to store clientid and clientname in redux and always access it from redux store, so instead of trying to route, you can update the data in redux store and that will trigger your componentWillReceiveProps function
Method 2:
One more solution would be to change the routing path, in case you have pathname like client - change the path to client/:clientid - if you use the routing in this way then you wont get the warning when you try to route from same component to same component
As what the warning said by react-router, you can't push to the same page.
Alternatively, you can/should use component's state, by updating the component state, it will rerender the component.
state = {
clientid: '',
clientname: ''
}
sendToMessage = (item) => {
this.setState({
clientid: item.clientid,
clientname: item.clientname
});
}

Resources