ReactJS - link to doesn't work for URL with parameter - reactjs

I use ReactJS and react-router-dom (version 4.2). I can't have my links working with :
<Link to={'/site/' + site.id}>
<h5><Icon name="map-marker"/> {site.title}</h5>
</Link>
It works when I am on home page (http://localhost:3000), the component is rendered, but when I am on a page like : http://localhost:3000/site/1, I can't go to URL http://localhost:3000/site/2.
The URL changes in address bar, but nothing happens.
I use :
...
<Route path="/site/:id" render={(props) => (
<SitesView id={props.match.params.id} />
)}/>
...
export default connect(mapStateToProps, mapDispatchToProps, undefined, { pure: false })(App);
In my Router. I can't get it, is it because the URL are almost the same ?

As I understood , you have one component <SitesView/> for all of the /site/ routes with different props.id's.
When you change the parameter of URL, the id of <SitesView/> component and the URL will be changed but be noticed the <SitesView/> is rendered on previous route and at the moment you are seeing this rendered component which has no knowledge aboute the changes.
Then you need to use one of the React LifeCycle Methods named componentWillReceiveProps(nextProp, nextState) to get to know when the id of <SitesView/> changed. and based on new id you can update your component.

Related

Conditionally enable Route in React

I have created an <Authorise /> component which renders either a 'yes' or 'no' prop based on a boolean 'allowed' prop.
export const Authorise = ({
allow = false,
yes = () => null,
no = () => null
}) => (allow ? yes() : no());
<Authorise /> component works as expected however when I try to render a <Route />, only the first route is rendered and the rest of the <Route /> components are ignored.
Why is this happening? If I render the routes outside of the <Authorise /> component it works fine. Only when rendering the components via the <Authorise /> component does it not work and I can't work out why.
Check out my example to see the issue:
CodeSandbox
It's because of
https://reactrouter.com/web/api/Switch/children-node
All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.
If you're using a library but not playing by its rule, then something like this is what was expected to happen.
The issue is not with your Authorize function, but because of the Route Component which will only display when the path is matched. The second issue it with Switch which will make it that only the first matched route to be displayed
With Switch added the "/" path will always be hit since it's the first one. If you try to remove your Switch (or move the route with "/" at the end) and go to "/next" on your local dev then the /next route will be displayed

React router two URL parameters &more

I'm using react-router-dom and this is currently how it looks:
<Switch>
<Route path={"/layouts/:layoutID"} component={Layouts} />
<Route
path={"/dashboard/:dashboardID"}
component={Dashboards}
/>
</Switch>
When the user navigates to "/dashboard/:dashboardID" inside this component he can choose a sub-page onClick, and I want the URL structure will be "/dashboard/:dashboardID/:pageID" pageID will navigate to 'PageIdComponent' this component will get a 'match' props and will show pageID
Please see the attached file that shows the necessary structure.
What is the best way to implement it?
If you have the child route inside your Dashboard component, then you can use the match.path value to build up a sub route:
<Route path={`${props.match.path}/:dashboardId`}>} component={SubDashboard} />
in order to make the path match what the parent's had and then add a new variable on top of that.
Then you can navigate to it using the match.url to make a link to the current URL and add the subPage to it:
<Link to={`${props.match.url}/pageFour`}>Page four</Link>
Instead of prop driling, React Router offers ways to get the relevant props to whatever components you desire via withRouter HOC or various hooks. The hooks were introduced in version 5.1
To get the match via a hook you can use const match = useRouteMatch()
To get the relevant props via the HOC:
const Example = useRouteMatch(function Example({ match, location, history }) {
return <div>{match.url}</div>;
});
For react router v6 (that's upcoming):
https://reacttraining.com/blog/react-router-v6-pre/#nested-routes-and-layouts

Component render() triggered after history.push but page is blank

I want the user to be redirected to the resources list after he deleted an item on its show page. I 've read a lot of SO Q&A on the topic, but I think I have a different issue as my routes and component got hit the right way after history.push
I tracked code execution through debugger till component render and still don't understand why nothing is returned
Here are my routes in my App component (wrapped this way<Router><App /></Router>) component :
<Route component={AppHeader} />
{["/articles/:id/edit", "/articles/new"].map((path, index) =>
<Route key={index} exact path={path} component{ArticleForm}/>
)}
<Route exact path="/articles/:id" component={Article}/>
{["/", "/articles"].map((path, index) =>
<Route key={index} exact path={path} component{ArticlesList}/>
)}
I use redux so Router and ArticleList are exported this way :
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Component))
In AppHeader component, a delete button is provided, if user is on show or edit page. When clicking on the link, following method is triggered :
class AppHeader extends Component {
...
deleteArticle = async () => {
await ajaxHelpers.ajaxCall('DELETE',`/articles/${this.state.currentArticleID}`, {}, this.state.token)
this.props.history.push("/")
}
...
}
Then Route with ArticlesList is triggered and should render this component. Here is what happens (breakpoints all the way in render methods):
URL is updated
Router is rendered
App header is rendered
Article list is rendered
Article list re-rendered with fecth from API (state populated with list)
Article list re-rendered with componentDidUpdate
BUT page stays blank ... I am using this.props.history.push("/") in other components and it works fine (list get re-rendered and displayed). See blank page and console.logs :
If I manually reload the page, it renders normally.
What is preventing any component to be displayed (DOM is nearly empty, I only get my empty <div id="root"></div>) in this case ?
Do one thing ,to displaying Article list use filter to update list state
deleteArticle = async () => {
await ajaxHelpers.ajaxCall('DELETE',`/articles/${this.state.currentArticleID}`, {}, this.state.token)
this.setState({articleList:articleList.filter((list)=> list.id!==this.state.currentArticleID)})
}
Change :
this.props.history.push("/")
To :
this.setState({articleList:articleList.filter((list)=> list.id!==this.state.currentArticleID)})

State change not re-rendering component

I couldn't find the answer anywhere because nothing was working for me, so I'm starting a new topic. Please, don't mark it as a duplicate
Router.js:
<Switch>
<Route path="/" exact component={Home} />
<Route exact path="/p/:uid" component={Profile} />
</Switch>
Profile.js
constructor(props) {
super(props);
this.state = {
loading: true,
profile_user_id: this.props.match.params.uid,
};
}
Then, later in Profile.js, I trigger a fetch to get data from backend and save this data to state using this.setState({ ... }). When the Profile component is rendered, everything looks fine.
In Router.js, there is also a Link:
<Link to={"/p/" + this.state.ntuser.tag}>Profile</Link>
.. which should go to your own profile. So when your user ID is 1, and you visit the profile of the user with id 22, your URL will be /p/user22 and the link will point to /p/user1.
The problem is that even though profiles are rendered nicely, Profile component does not become re-rendered when you click the link (which should direct you to /p/user1 and show your profile instead). I tried to save location from react-router to state as well, so every time URL changes it will be caught in componentWillReceiveProps() and inside I update state. But still nothing. Any ideas?
PS: I'm using React.Component
console.log(this.props.match.params.uid) in constructor, componentDidMount() and componentDidUpdate() (UNSAFE_componentWillReceiveProps() is deprecated)
Number and places (of log calls) will tell you if component is recreated (many construcor calls) or updated (cDM calls). Move your data fetching call accordingly (into cDM or cDU ... or sCU).
You can save common data in component above <Router/> (f.e. using context api) - but this shouldn't be required in this case.
Solution
You can update state from changed props using componentDidUpdate() or shouldComponentUpdate(). componentDidUpdate should be protected with conditions to prevent infinite loop. See docs.
Simplest solution:
componentDidUpdate(prevProps) {
if (this.props.some_var !== prevProps.some_var) {
// prop (f.e. route '.props.match.params.uid') changed
// setState() - f.e. for 'loading' conditional rendering
// call api - use setState to save fetched data
// and clearing 'loading' flag
}
}
shouldComponentUpdate() can be used for some optimalizations - minimize rerenderings.
Had same problem in my project, Didn't find any good workable idea exept making somethink like this:
import {Profile} from "../Profile "
<Link to={`your href`}
onClick={userChanged}/>
..........
function userChanged() {
const userCard= new Profile();
userCard.getProfileData();
}
First of all you need to make your Profile component as export class Profile extends React.Component (export without default).
getProfileData is method where i get data from my api and put it state. This will rerender your app
Here is what happens:
You go to /p/user22.
React renders <Route exact path="/p/:uid" component={Profile} />.
The Route renders the Profile component for the first time.
The Profile component calls the constructor, at the time, this.props.match.params.uid is equal to "user22". Therefore, this.state.profile_user_id is now "user22".
You click on the link to /p/user1.
React renders <Route exact path="/p/:uid" component={Profile} />, which is the same Route.
The Route then rerenders the same Profile component but with different props.
Since, its the same Profile component, it does not update the state.
This explains why you still see the profile of user22

React. How to redirect early in a component lifecycle

This seems so simple, but I am new to react and trying different approaches and nothing is working for me. (BTW I am a regular contributor but working on my clients machine and can't remember my so password.)
The version of react router is 4.0, and state is stored using redux.
The scenario is that we are changing the order of routing in our application and would like to redirect any users that have Urls with the old structure to the new Url structure. I have tried the following (also note that I have "scrubbed" the names of the page, function calls and variables):
There is a trigger component for the section I need to direct from, with routing set up like this:
<Route path='page/:pageGuidGuid' component={PageTrigger}> </Route>
In the trigger component, ComponentWillMount makes a request that returns a 404 if the link is from the previous component, although it does redirect to the correct route. I am checking for and triggering the redirect in getInitialState, but the component keeps going through the lifecycle and ComponentWillMount is called, resulting in a 404 and then redirects to the correct page.
const PageTrigger = connectToStores(React.createClass({
getInitialState() {
this.checkForRedirect();
return {};
},
componentWillMount() {
if (this.props.params.guid) {
this.setCaseByGuid(this.props.params.guid);
}
},
checkFrorRedirect() {
/* logic to determine if it should redirect */
browserHistory.push(redirectUrl);
}
}
I have also tried creating a custom route...
<CaseRedirectRoute path='(cases/:caseGuid)' component={CaseTrigger}>
</CaseRedirectRoute>
And in a separate module (based off a login sample)
const CaseRedirectRoute = ({ component: Component, ...rest }) => (
<Route
{...rest} render={props => (
checkForCaseRedirect(...props) ? (
<Redirect to={{
pathname: getCaseUrlRedirectUrl(...props),
state: { from: props.location }
}} />
) : (
<Component {...props} />
)
)} />
);
I have WithRouter imported from react router. When I try to run this I am getting an error inside the react framework:
Uncaught Type Error: Cannot read property 'createRouteFromReactElement' of undefined
at RouteUtils.js:68
at forEachSingleChild (ReactChildren.js:52)
at traverseAllChildrenImpl (traverseAllChildren.js:98)
at traverseAllChildrenImpl (traverseAllChildren.js:114)
at traverseAllChildren (traverseAllChildren.js:186)
at Object.forEachChildren [as forEach] (ReactChildren.js:70)
at createRoutesFromReactChildren (RouteUtils.js:65)
at Function.createRouteFromReactElement (RouteUtils.js:35)
at RouteUtils.js:69
at forEachSingleChild (ReactChildren.js:52)
I've tried redirecting from app.js and page.js, but the PageTrigger is the first component having state set. I either need to figure out how to stop execution after the redirect, or figure out why my custom route keeps blowing up. Any help would be appreciated.
I'm not sure about your setup, but I would implement this redirect as this:
<Route path='oldurl/:p1/:p2' render={
routeProps => (
<Redirect to={'/newUrlHere/'+routeProps.match.params.p1} />
)
} />
So, if the route matches (you may specify exact match if necessary), when the Redirect "renders" it stops rendering and should start the rendering from the new URL. You may build your URL in the Redirect.to property as you wish

Resources