I currently have a few routing paths in my code which pass in a number of properties to those views. As below:
import React, {Component} from 'react'
import { Route, Switch, Redirect, withRouter } from 'react-router-dom'
import CalendarView from '../calendarView'
import ListView from '../listView'
import AgendaView from '../agendaView'
import propTypes from 'prop-types'
class DiaryRouting extends Component{
render() {
const activities = this.props.activities
return (
<switch>
<Route exact path="/my-diary/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/activities/" render={() => <Redirect push to="/my-diary/activities/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
<Route exact path="/my-diary/jobs/" render={() => <Redirect push to="/my-diary/jobs/calendar-view/month" component={(props) => <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='jobs' activities={activities}/>} /> } />
</switch>
)
}
}
DiaryRouting.propTypes = {
activities: propTypes.array,
}
export default DiaryRouting
I have items being passed in such as selectedViewRange and selectedDiaryType into each route. What I am trying to accomplish to having a variable in this view that holds what route it has gone through and what variable has been passed in. Such as below:
....
state = {
selectedViewRange: null
}
... <Route exact path="/my-diary/activities/"
render={() => <Redirect push to="/my-diary/activities/calendar-view/month"
component={(props) => this.setState(selectedViewRange: 'calendar-view') <CalendarView {...props} selectedViewRange = 'calendar-view' selectedViewType='month' selectedDiaryType='activities' activities={activities}/>} /> } />
However I keep getting a
Warning: setState(…): Cannot update during an existing state
transition
I have tried using a variable instead of a state and that does not do anything.
Best approach how to tackle this?
First off, I'd strongly suggest to use redux for app state management because setState has never been a reliable and scaleable choice.
Anyways, to answer your question:
For the class component that holds your state for activeRoute (rephased your selectedViewRange for better understanding), you should define a method like:
setActiveRoute = (activeRoute) => { this.setState({ activeRoute }) }
Pass it as a prop to the components that you render in your routes.
So basically, your CalendarView should have an extra prop like setActiveRouteName and you should pass this.setActiveRoute as a callback to setActiveRouteName prop like:
<CalendarView {...props} setActiveRouteName={this.setActiveRoute} ... />
Moving on, in your CalendarView, declare a React lifecycle method componentDidMount. It should look like:
export default class CalendarView extends Component {
componentDidMount(){
this.props.setActiveRouteName('yourRouteName');
}
...other code...
}
This should solve the problem.
Related
I have code like this:
<BrowserRouter basname="/page">
<Switch>
<Route path="/test/:id">
<Page />
</Route>
</Switch>
</BrowserRouter>
When i switch from /page/test/1 to /page/test/2 the Page component won't re-rendering. I know the componentDidMount method won't be called but i want the Page component re-render.
How can i do that?
Try like this if you are using React v16.8 or above
import React, { useEffect } from "react";
const Page = (props) => {
useEffect(() => {
//Your code comes here to fetch the data from API
}, [props.match.params.id]);
return (<div>Layout</div>);
}
export default Page;
For Class components
<Route path="/page/:pageid" render={(props) => (
<Page key={props.match.params.pageid} {...props} />)
} />
I am a beginner for React. I just need to get the path param from the URL when the page is loaded. My URL is as follows.
http://localhost:3000/explorer/test/111
I have set the route like following,
<Route
exact path="/explorer/test/:id"
component={() => <BlockPage />}
/>
Then in the <BlockPage /> component I need to get the value of :id when the component is loading. Therefore, I added the following code segment to get an id, but it gives me error as in the title.
import React, { Component } from 'react';
import NavigationComp from './Navigation';
import { withRouter } from 'react-router-dom';
const BlockPage = () =>
<div>
<NavigationComp/>
<BlockData />
</div>
class BlockData extends Component {
componentDidMount() {
console.log(this.props.match.params.id);
}
render(){
return(.....)
}
}
export default withRouter(BlockPage);
What is the mistake I did? Can someone explain to me? my objective is to get 111 value from URL into componentDidMount()
when you want to use component you dont need function like you code you can change it like it :
<Route
exact path="/explorer/test/:id"
component={<BlockPage />}
/>
or use render function if you want a function like this
<Route
exact path="/explorer/test/:id"
render={(props) => <BlockPage {...props} />}
/>
then in this.props.match.params you can access your id.
This happens because you the component is sub component of other component which ReactRouter knows. For example,
<BrowserRouter>
<Switch>
<Route path="/Feedback">
<Switch>
<BrowserRouter>
in the above route defnition, the match object in Feedback component is not null, but if we have other component in feedback and tries to access it it's an error.
https://stackoverflow.com/a/57628327/12378703
this works perfectly... try this out
<Route
exact path="/explorer/test/:id"
render={(props) => <BlockPage {...props} />}
/>
This solved my problem:
<CPAroute exact path="/advcampaigns/:id" component={AdvCampaigns}/>
class AdvCampaigns extends React.Component {
constructor(props) {
super(props);
this.state = {
activePage: 1
};
};
componentDidMount() {
console.log(this.props.**computedMatch**.params.id);
}
}
I also faced same problem.
Tried all the above solutions, but didn't work.
Then I just console logged this.props
const productName = this.props.params.<YOUR_DYNAMIC_URL>;
WITHOUT match in this.props
And it worked, don't know how but it worked.
May be helpful for others.
i want redirect to "/user". i write but this not work.
how to correctly redirect to the right page
onClick = (e) => {
this.setState({ errorLoad: false});
getPlayerInfo(this.state.id).then(data => {
if(data.success == false) {
this.setState({ errorLoad: true});
return;
}
this.setState({ user: data.player});
console.log(data);
<Redirect to="/user"/>
});
}
My router list. Among them there is a router with the path "/ user"
<Route path="/user" render={(props) => <User {...props} user={this.state.user} />} />
UPADATE
App.js
The button I click on is in the component <SearchForm/>
render() {
let style = {marginLeft: '20px'};
return (
<div>
<Header source='https://www.shareicon.net/data/2017/02/15/878753_media_512x512.png'/>
<SearchForm onClick={this.onClick} style={style} onChange={this.onHandle} placeholder="search"/>
<Centered style={ {marginTop: '50px'} }>
<BrowserRouter>
<Switch>
<Route exact path='/' component={Startup} />
<Route path="/user" render={(props) => <User {...props} user={this.state.user} />} />
</Switch>
</BrowserRouter>
</Centered>
</div>
);
}
There are two ways to programmatically navigate with React Router - <Redirect /> and history.push. Which you use is mostly up to you and your specific use case.
<Redirect /> should be used in user event -> state change -> re-render order.
The downsides to this approach is that you need to create a new property on the component’s state in order to know when to render the Redirect. That’s valid, but again, that’s pretty much the whole point of React - state changes update the UI.
The real work horse of React Router is the History library. Under the hood it’s what’s keeping track of session history for React Router. When a component is rendered by React Router, that component is passed three different props: location, match, and history. This history prop comes from the History library and has a ton of fancy properties on it related to routing. In this case, the one we’re interested is history.push. What it does is it pushes a new entry onto the history stack - aka redirecting the user to another route.
You need to use this.props.history to manually redirect:
onClick = (e) => {
this.setState({ errorLoad: false});
getPlayerInfo(this.state.id).then(data => {
if(data.success == false) {
this.setState({ errorLoad: true});
return;
}
this.setState({ user: data.player});
console.log(data);
this.props.history.push('/user');
});
}
You should be getting history as a prop from your <Router> component.
EDIT:
Okay thank you for the code update. The SearchForm component is not nested under your BrowserRouter, so it is not getting the history prop. Either move that component inside the BrowserRouter or use the withRouter HOC in SearchForm reacttraining.com/react-router/web/api/withRouter
Option 1: Move SearchForm inside the BrowserRouter
render() {
let style = {marginLeft: '20px'};
return (
<div>
<Header source='https://www.shareicon.net/data/2017/02/15/878753_media_512x512.png'/>
<Centered style={ {marginTop: '50px'} }>
<BrowserRouter>
<SearchForm onClick={this.onClick} style={style} onChange={this.onHandle} placeholder="search"/>
<Switch>
<Route exact path='/' component={Startup} />
<Route path="/user" render={(props) => <User {...props} user={this.state.user} />} />
</Switch>
</BrowserRouter>
</Centered>
</div>
);
}
Option 2: use the withRouter HOC to inject the history prop into SearchForm manually:
import { withRouter } from 'react-router-dom';
class SearchForm extends React.Component { ... }
export default withRouter(SearchForm)
I have several routes rendering the same component. Depending on the route I want the component to fetch different data. However since I keep rendering the same component, React doesn't see any changes to the DOM when I click a Link tag (from my nav bar located in the Layout component) to another route rendering that same component. Meaning the component is not re-rendered with the new data. Here are my routes:
class App extends Component {
render() {
return (
<BrowserRouter>
<Provider store={store}>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/fashion" component={PostTypePageContainer} />
<Route exact path="/beauty" component={PostTypePageContainer} />
</Switch>
</Layout>
</Provider>
</BrowserRouter>
);
}
}
export default App;
Here is the PostTypePageContainer component that I want to re-render with the new data each time:
class PostTypePageContainer extends Component {
componentDidMount() {
let route;
switch (this.props.location.pathname) {
case '/fashion':
route = '/fashion';
break;
case '/beauty':
route = '/beauty';
break;
default:
console.log('No data was found');
}
let dataURL = `http://localhost:8888/my-site//wp-json/wp/v2${route}?_embed`;
fetch(dataURL)
.then(res => res.json())
.then(res => {
this.props.dispatch(getData(res));
});
}
render() {
let posts = this.props.postData.map((post, i) => {
return <PostTypePage key={i} props={post} />;
});
return <div>{posts}</div>;
}
}
const mapStateToProps = ({ data }) => ({
postData: data.postData
});
export default connect(mapStateToProps)(PostTypePageContainer);
How do I go about re-rendering that component each time?
This is intended behavior of react-router.
While i suggest you create a HOC to fetch the data from different locations and pass it to the PostTypePageContainer via props, using a key will give you a quick work around that will cause your component to remount.
class App extends Component {
render() {
return (
<BrowserRouter>
<Provider store={store}>
<Layout>
<Switch>
<Route exact path="/" component={Home} />
<Route exact key={uniqueKey} path="/fashion" component={PostTypePageContainer} />
<Route exact key={someOtherUniqueKey} path="/beauty" component={PostTypePageContainer} />
</Switch>
</Layout>
</Provider>
</BrowserRouter>
);
}
}
export default App;
Source: https://github.com/ReactTraining/react-router/issues/1703
I wasn't able to get the <Route key={...} ... /> to work in my case. After trying several different approaches the one that worked for me was using the componentWillReceiveProps function in the reused component. This was getting called each time the component was called from a <Route />
In my code, I did:
componentWillReceiveProps(nextProps, nextContext) {
// When we receive a call with a new tag, update the current
// tag and refresh the content
this.tag = nextProps.match.params.tag;
this.getPostsByTag(this.tag);
}
I am new to React and I am working on the search functionality of a site. I am using a create-react-app base.
Here is the routing:
<BrowserRouter>
<App>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/blah' component={Blah} />
<Route path='/boop' component={Boop} />
<Route path="/search/:searchTerm?" component={Search} />
<Route path="*" component={NotFound} />
</Switch>
</App>
</BrowserRouter>
Note the search one has an optional parameter. I am picking up this parameter fine in Search.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import SearchResults from './SearchResults';
export default class Search extends Component {
constructor(props) {
super(props);
this.state = {
searchTerm: this.props.match.params.searchTerm
}
}
render() {
return (
<div className="Search">
SEARCH: {this.state.searchTerm}
<SearchResults searchTerm={this.state.searchTerm} />
</div>
);
}
}
Over in the actual form that triggers the search, this snippet handles the click:
handleClick(event) {
this.props.history.push('/search/'+this.state.value);
}
This works and displays results for paths like /search/test and /search/woo. The only problem is when I go directly from one search to another.
/search/test -> /search/woo updates the path in the address bar, but does not render the content of the woo page with the new search results.
Going to another path between makes it work. So /search/test -> /boop -> /search/woo all works as expected.
Am I missing something here? Do I need to manually trigger this somehow, or should optional parameter changes trigger the components to update?
Thanks!
You need to sync state to props on every change if you want to store this term in component's state.
export default class Search extends Component {
...
componentWillReceiveProps(nextProps) {
const newSearchTerm = nextProps.match.params.searchTerm
if(this.state.searchTerm !== newSearchTerm) {
this.setState(() => ({ searchTerm: newSearchTerm }))
}
}
...
}
But there is no reason to do it. Use this.props in render. It could be as simple as this
export default ({ match: { params: { searchTerm } } }) => (
<div className="Search">
SEARCH: {searchTerm}
<SearchResults searchTerm={searchTerm} />
</div>
)