25

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;

3
  • 1
    Can you should your Log component Commented Jul 19, 2018 at 8:14
  • I added the log component. the function applyFilter calls history.push. As mentioned, URL gets updated but component is not re-rendering.. I expect a re-render so that constructor is called again and I can get new data. Is that the right approach? Thanks. Commented Jul 19, 2018 at 8:29
  • 1
    A re-render is trigger on the query params change, but not a remount so constructor is not called again. If you want to detect a change in query params, check this stackoverflow.com/questions/48993247/… Commented Jul 19, 2018 at 9:33

3 Answers 3

11

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)

Sign up to request clarification or add additional context in comments.

Comments

9

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 componentDidMountand componentDidUpdate.

3 Comments

I tried to use this validation of yours, but I was not successful
Do you have any other suggestions for what might cause the screen to be rendered?
This is correct answer but in this case API calling twice in every render.
0

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>
    );
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.