React: URL Profile ID doesn't match (match.params.id) - reactjs

So whenever I clicked the View Profile Link
<Link to={`/profile/${_id}`} className="btn btn-primary">
View Profile
</Link>
It shows in the URL the User ID of the Profile which is Good.
But whenever I Clicked it to Match it with this Button I get an error.
const Profile = ({ getProfileById, match }) => {
useEffect(() => {
getProfileById(match.params.id);
}, [getProfileById]);
return <div>test</div>;
};
I get on my console
React Hook useEffect has a missing dependency: 'match.params.id'. Either include it or remove the dependency array
this is what is in my app Js.
<Route
exact
path="/profile/:id"
component={Profile}
/>
I think it doesn't match with the URL to the button I clicked.
and inside the Redux Devtools it only returns a Profile Error.

In this case, the linter rule which is producing the warning: React Hook useEffect has a missing dependency: 'match.params.id'. Either include it or remove the dependency array, is telling you that you have another dependency. Use this instead:
const Profile = ({ getProfileById, match }) => {
useEffect(() => {
getProfileById(match.params.id);
}, [getProfileById, match.params.id]);
return <div>test</div>;
The linter doesn't explore your whole app to determine where and how match is used. It also couldn't know how you intend to use it in the future, as a result, it asks that you put it in the dependency array along with getProfileId.
If you had imported getProfileId or placed it outside of the component instead of passing it down as a prop, you could safely remove getProfileId from the dependencies. All of this is to say that figuring out what goes inside the array takes a bit of additional understanding. Dan Abramov wrote a great blog post that helped me wrap my head around when and how to use the dependency array. This post might be a good place to start, if you find Dan's post a bit too broad since it deals with more generalized concepts of writing resilient components.

I managed to figure out what's wrong. It's in my API. I am trying to hit the .get in my API
But inside my API it is indicated the patch.
and I changed it to get.
and it works.

Related

React: is this an acceptable way to add event listeners to a functional component?

i'm working on my first app using react and i want to attach a event listener to a component. I know theres a lot of ways to do this but i came up with this one:
function loginHandler () {
alert('Hey, you clicked me!');
}
const Header = () => {
return (
<header className="header">
<nav className="home-navbar">
<a className="home-navbar__link home-navbar__logo" href="/">Notepp</a>
<a className="home-navbar__link" href="/">About</a>
<a onClick={loginHandler} className="home-navbar__link home-navbar__login" href="/">Sign in with Google</a>
</nav>
</header>
)
}
I don't want to transform this into a class component and create loginHandler method because this is just a simple nav component and i feel like it would somehow complicate thinks a bit. I'm also not sure on how to use hooks although i heard they are very useful. Of course i'm would separate my loginHandler function into her own file but i just want to hear from more experienced developers first. Now this being said i have actually 2 questions.
Is this okay to do?
On the lessons i took from React, the teachers always used class components and onClick to pass event listeners to components, and i decided to create my own app before learning hooks so...
Should i use onClick to handle events?
People always say its best to separate markup from interactivity and i agree with them, and seeing as onClick is at its core (i think) an HTML event handler, i'm a bit suspicious of them (don't wanna hurt my imaginary good practice points right?). Thanks in advance for your help.
Of course, your code is fine, React itself encourages using functional components (with the addition of hooks, memo, ...).
This is almost an exact copy of the code in the official React documentation for Handling Events:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
I'd suggest at least going through "Main concepts" of the official documentation, before developing your first application. It also has a section on Function and Class Components.
The only remark I could make about this code is that if the onClick will take you to a different screen, then you might look at something like react-router so the URL changes as well.

Changing Query paramers while staying on the same page without reload= NextJS Router

for a project I am working on I am running into a problem with the nextjs Router.I have a component that has an input field which the user should be able to input their searchterm in. There is a different component which should be able to get this searchterm and perform a search.
Because the two components aren't connected I would like to set the queryParameters in the router in the Input component, and then execute a function in the search component when the searchTerm is changed.
The problem lies in the following: The searchComponent receives the nextJS router as props and will only execute my useEffect function when those props are changed (and react knows they are changed), on top of that I need to stay on the same page when updating the query parameters, but the route of this page is dynamic. For example: the user can add this combination of components on /search but also on /lookforitem.
I have tried setting the queryParameters in the following way in the Input component:
function setQueryParams() {
router.query = {
...router.query,
searchTerm: input.current,
};
}
In combination with the following code in the Search component:
useEffect(() => {
console.log('Router has changed');
}, [router]);
The problem is that this useEffect doesnt get called untill the search component is rendered again (I have created a button that logs the router to the console, and it shows the updated router), which I assume is because React hasn't realised that the Router props have changed.
I have also tried setting the query parameters via a router.push in the following way:
function setQueryParams() {
router.push(
{
pathname: router.route,
query: {
...router.query,
searchTerm: input.current,
},
},
undefined,
{ shallow: true }
);
}
However this comes with its own set of problems. First of all it causes a refresh of the page, which I don't want. On top of that it changes the url to for example: /search?searchTerm=Hello which means that if I enter a different input and submit it will stack making the next url for example: &searchterm=hello?searchterm=goodbye.
I want a way to update the query parameters without refreshing the page, but while also notifying the other components that use the router that the query parameters have updated. All of the searching that I've done seems to be specific to either routing to a different page or routing to a predefined page.
Any help would be greatly appreciated.

React Navigation 4.x to React Navigation 5.x

The current problem I have is that i want to use navigation parameters to update the state
The tutorial in the link above uses React Navigation 4.x while I use React Navigation 5.x
Tutorial:
function onSaveNote() {
navigation.state.params.addNote({ noteTitle, noteValue })
navigation.goBack()
}
MyProject:
function onSaveAuction() {
navigation.navigate('Home', { auctionTitle, auctionValue }
}
This is the warning I would get whenever I used used the code for 4.x
I have tried using the second bullet point which is to use navigate instead but it still does not seem to work.
Any help would be appreciated.
There is nothing wrong with the syntax(except for the bracket you forgot to close). Your problem is with the data you are trying to pass. The warning tells you that you are trying to pass non-serializable values such as class instances, functions etc. So check again what are the values of auctionTitle and auctionValue.
We don't know your data, however you shouldn't pass functions or class in nav params.
To make sure that your data doesn't have non-serializable data, as mentioned above, you can try do a JSON.stringify(), then JSON.parse in next screen to see if this warning disappears.
The best solution is to check your data, but if you need to pass non-serializable data, feel free to use JSON.
I made a example to you:
Passing params:
function onSaveAuction() {
/* It will remove any functions, class or other non-serializable from params. */
const data = JSON.stringify({ auctionTitle, auctionValue });
navigation.navigate('Home', { data });
}
Home.js
function Home({ route, navigation }) {
/* Get the param, then parse to object */
const data = JSON.parse(route.params.data);
}

How to Jump to Redux State with Browser Back Button

I’m a react beginner and I have a project using React Router (4.2.2), Redux and I’ve recently added react-router-redux hoping that it would solve my problem. I have components whose redux stored states need to change based on interactions with the browser back and forward buttons.
For example, I have a screen containing details for an element the user clicked. If I use the browser back button and navigate to another point in history in which that details view was open, it will only show the most recent element info (due to that the only information in the store) or sometimes no information at all will be passed.
I thought react-router-redux would help me keep these two in sync but maybe I’m missing a step that enables that to happen. I’ve installed Redux Debug Tools and I can see in there the state that I want to jump to, but how do I enable it when the user uses the back button? The url for the details page is the same each time it’s viewed, perhaps I need to add a hash tag with specific information but even then, how would I do a look up for the correct information in the store?
I open the details view like this:
<Link to='/activityDetails'>
<ActivityButton
id={this.props.someData.someId}
/>
</Link>
And inside ActivityButton:
openActivityDetails = () => {
this.props.showActivityDetailsScreen(this.props.id);
};
function matchDispatchToProps(dispatch) {
return bindActionCreators({
showActivityDetailsScreen: activityDetailsActions.showActivityDetails
}, dispatch)
}
render(){
return (
<div className={'activity-button'} onClick={this.openActivityDetails}>
<img
onClick={this.handleClick}
src={imgPath}
/>
</div>
);
}
So I figured it out on my own. Essentially what you need to do is set the state information inside the when you create it, like so:
<Link to={{
pathname: '/activityDetails',
state: { "activityData": activityData }
}}>
<ActivityButton
id={this.props.someData.someId}
/>
</Link>
then in the class you need the information ActivityDetails in my case you can query it like this:
var actData = this.props.location.state.activityData;
or if you're using withRouter and history it's also in the following location:
const { history: { push } } = this.props;
history.state.state.activityData
I ended up removing react-router-redux because it wasn't necessary. It made an entry in the state with the current route, but in my case that wasn't useful. Hope this helps someone in the future.

How to prevent route change using react-router

There's a certain page in my React app that I would like to prevent the user from leaving if the form is dirty.
In my react-routes, I am using the onLeave prop like this:
<Route path="dependent" component={DependentDetails} onLeave={checkForm}/>
And my onLeave is:
const checkForm = (nextState, replace, cb) => {
if (form.IsDirty) {
console.log('Leaving so soon?');
// I would like to stay on the same page somehow...
}
};
Is there a way to prevent the new route from firing and keep the user on the same page?
It is too late but according to the React Router Documentation you can use preventing transition with helping of <prompt> component.
<Prompt
when={isBlocking}
message={location =>
`Are you sure you want to go to ${location.pathname}`
}
/>
if isBlocking equal to true it shows a message. for more information you can read the documentation.
I think the recommended approach has changed since Lazarev's answer, since his linked example is no longer currently in the examples folder. Instead, I think you should follow this example by defining:
componentWillMount() {
this.props.router.setRouteLeaveHook(
this.props.route,
this.routerWillLeave
)
},
And then define routerWillLeave to be a function that returns a string which will appear in a confirmation alert.
UPDATE
The previous link is now outdated and unavailable. In newer versions of React Router it appears there is a new component Prompt that can be used to cancel/control navigation. See this example
react-router v6 no longer supports the Prompt component (they say that they hope to add it back once they have an acceptable implementation). However, react-router makes use of the history package which offers the following example for how to block transitions.
Note that to actually make this work in react router you have to replace the createBrowserHistory call with some hackery to make sure you are using the same history object as react router (see bottom of answer).
const history = createBrowserHistory();
let unblock = history.block((tx) => {
// Navigation was blocked! Let's show a confirmation dialog
// so the user can decide if they actually want to navigate
// away and discard changes they've made in the current page.
let url = tx.location.pathname;
if (window.confirm(`Are you sure you want to go to ${url}?`)) {
// Unblock the navigation.
unblock();
// Retry the transition.
tx.retry();
}
You'll need to put this inside the appropriate useEffect hook and build the rest of the functionality that would have otherwise been provided by prompt. Note that this will also produce an (uncustomizable) warning if the user tries to navigate away but closing the tab or refreshing the page indicating that unsaved work may not be saved.
Please read the linked page as there are some drawbacks to using this functionality. Specifically, it adds an event listener to the beforeunload event which makes the page ineligable for the bfcache in firefox (though the code attempts to deregister the handler if the navigation is cancelled I'm not sure this restores salvageable status) I presume it's these issues which caused react-router to disable the Prompt component.
WARING to access history in reactrouter 6 you need to follow something like the instructions here which is a bit of a hack. Initially, I assumed that you could just use createBrowserHistory to access the history object as that code is illustrated in the react router documentation but (a bit confusingly imo) it was intended only to illustrate the idea of what the history does.
We're using React Router V5, and our site needed a custom prompt message to show up, and this medium article helped me understand how that was possible
TLDR: the <Prompt/> component from react-router-dom can accept a function as the message prop, and if that function returns true you'll continue in the navigation, and if false the navigation will be blocked
React-router api provides a Transition object for such cases, you can create a hook in a willTransitionTo lifecycle method of the component, you are using. Something like (code taken from react-router examples on the github):
var Form = React.createClass({
mixins: [ Router.Navigation ],
statics: {
willTransitionFrom: function (transition, element) {
if (element.refs.userInput.getDOMNode().value !== '') {
if (!confirm('You have unsaved information, are you sure you want to leave this page?')) {
transition.abort();
}
}
}
},
handleSubmit: function (event) {
event.preventDefault();
this.refs.userInput.getDOMNode().value = '';
this.transitionTo('/');
},
render: function () {
return (
<div>
<form onSubmit={this.handleSubmit}>
<p>Click the dashboard link with text in the input.</p>
<input type="text" ref="userInput" defaultValue="ohai" />
<button type="submit">Go</button>
</form>
</div>
);
}
});

Resources