Reputation: 497
In my component I use this.props.history.push(pathname:.. search:..) to rerender the component and fetch new data form a third party service. When I first call the page it renders. But when I call history push inside the component the URL updates correctly BUT the component doesn't rerender. I read a lot but couldn't get it working. Any ideas?
I'm using react router v4
//index.js
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/login" component={Login}/>
<Route path="/" component={Main}/>
</Switch>
</BrowserRouter>
</Provider>
//Main.js
//PropsRoute is used to push props to logs component so I can use them when fetching new data
const PropsRoute = ({ component: Component, ...rest }) => {
return (
<Route {...rest} render={props => <Component {...props} />}/>
);
};
class Main extends Component {
render() {
return (
<div className="app">
<NavigationBar/>
<div className="app-body">
<SideBar/>
<Switch>
<PropsRoute path="/logs" component={Log}/> //this component is not rerendering
<Route path="/reports" component={Reports}/>
<Route path="/gen" component={Dashboard}/>
<Redirect from="/" to="/gen"/>
</Switch>
</div>
</div>
)
}
}
export default Main;
//inside 'Log' component I call
import React, {Component} from 'react';
import {getSystemLogs} from "../api";
import {Link} from 'react-router-dom';
import _ from "lodash";
import queryString from 'query-string';
let _isMounted;
class Log extends Component {
constructor(props) {
super(props);
//check if query params are defined. If not re render component with query params
let queryParams = queryString.parse(props.location.search);
if (!(queryParams.page && queryParams.type && queryParams.pageSize && queryParams.application)) {
this.props.history.push({
pathname: '/logs',
search: `?page=1&pageSize=25&type=3&application=fdce4427fc9b49e0bbde1f9dc090cfb9`
});
}
this.state = {
logs: {},
pageCount: 0,
application: [
{
name: 'internal',
id: '...'
}
],
types: [
{
name: 'Info',
id: 3
}
],
paginationPage: queryParams.page - 1,
request: {
page: queryParams.page === undefined ? 1 : queryParams.page,
type: queryParams.type === undefined ? 3 : queryParams.type,
pageSize: queryParams.pageSize === undefined ? 25 : queryParams.pageSize,
application: queryParams.application === undefined ? 'fdce4427fc9b49e0bbde1f9dc090cfb9' : queryParams.application
}
};
this.onInputChange = this.onInputChange.bind(this);
}
componentDidMount() {
_isMounted = true;
this.getLogs(this.state.request);
}
componentWillUnmount() {
_isMounted = false;
}
getLogs(request) {
getSystemLogs(request)
.then((response) => {
if (_isMounted) {
this.setState({
logs: response.data.Data,
pageCount: (response.data.TotalCount / this.state.request.pageSize)
});
}
});
}
applyFilter = () => {
//reset page to 1 when filter changes
console.log('apply filter');
this.setState({
request: {
...this.state.request,
page: 1
}
}, () => {
this.props.history.push({
pathname: '/logs',
search: `?page=${this.state.request.page}&pageSize=${this.state.request.pageSize}&type=${this.state.request.type}&application=${this.state.request.application}`
});
});
};
onInputChange = () => (event) => {
const {request} = this.state; //create copy of current object
request[event.target.name] = event.target.value; //update object
this.setState({request}); //set object to new object
};
render() {
let logs = _.map(this.state.logs, log => {
return (
<div className="bg-white rounded shadow mb-2" key={log.id}>
...
</div>
);
});
return (
<main className="main">
...
</main>
);
}
}
export default Log;
Upvotes: 25
Views: 30326
Reputation: 851
I had this same issue with a functional component and I solved it using the hook useEffect with the props.location
as a dependency.
import React, { useEffect } from 'react';
const myComponent = () => {
useEffect(() => {
// fetch your data when the props.location changes
}, [props.location]);
}
This will call useEffect
every time that props.location
changes so you can fetch your data. It acts like a componentDidMount
and componentDidUpdate
.
Upvotes: 9
Reputation: 627
what about create a container component/provider with getderivedstatefromprops
lifecycle method, its more react-look:
class ContainerComp extends Component {
state = { needRerender: false };
static getderivedstatefromprops(nextProps, nextState) {
let queryParams = queryString.parse(nextProps.location.search);
if (!(queryParams.page && queryParams.type && queryParams.pageSize && queryParams.application)) {
return { needRefresh: true };
} else {
return { needRefresh: false };
}
}
render() {
return (
<div>
{this.state.needRefresh ? <Redirect params={} /> : <Log />}
</div>
);
}
}
Upvotes: 0
Reputation: 571
Reactjs don't re-run the constructor
method when just props
or state
change, he call the constructor
when you first call your component.
You should use componentDidUpdate
and do your fetch if your nextProps.location.pathname
is different than your this.props.location.pathname
(react-router location)
Upvotes: 11