Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

componentDidMount not running after calling browserHistory.push("/url")

I have an issue where I will send a user to a react-router route after login, based on the following:

        ...
        //check login
        browserHistory.push(self.props.destination_url);

I was expecting componentDidMount to run, since this component had not been on the screen since I loaded the app, but it will not. If I click a link to it (react-router link) in the nav bar, componentDidMount does run however.

I just need to make an API call when this component comes on the screen because of a browserHistory.push(self.props.destination_url); route change. I've tried things like

<Router createElement={ (component, props) =>
{
  const { location } = props
  const key = `${location.pathname}${location.search}`
  props = { ...props, key }
  return React.createElement(component, props)
} }/>

here Component does not remount when route parameters change and it isn't working.

Here http://busypeoples.github.io/post/react-component-lifecycle/ shows "on mount", "on unmount", "on state change", or "on props changes". I'm not seeing any of those apply here. Is there a lifecycle method that will run after this browserHistory push transition?

I've been trying random lifecycle methods and componentWillUpdate does run after browserHistory.push but it runs hundreds of times, completely slowing the app down. I'd assume something I did inside it caused the nearly infinite loop:

componentWillUpdate() {
    console.log('it ran componentWillUpdate');
    if (this.props.email) {

        console.log('firing off /api/userInfo');
        let self = this;
        axios.post('/api/userInfo', {email: this.props.email})
          .then(function (response) {
              let result = response.data.result;
              console.log('after calling /api/userInfo');
              console.log(response);
              console.log(result);
              if (result) {
                  self.setState({restaurant_profile: result});
              }
          })
          .catch(function (error) {
              console.log("Something went wrong trying to check for a user's restaurant profile");
              console.log(error);
          });
    }
}

On the server/client you now see the POST run hundreds of times:

Executing (default): SELECT `id`, `email`, `password`, `RestaurantId` FROM `Users` AS `User` WHERE `User`.`email` = '[email protected]' LIMIT 1;

Executing (default): SELECT `id`, `email`, `password`, `RestaurantId` FROM `Users` AS `User` WHERE `User`.`email` = '[email protected]' LIMIT 1;

Executing (default): SELECT `id`, `email`, `password`, `RestaurantId` FROM `Users` AS `User` WHERE `User`.`email` = '[email protected]' LIMIT 1;

Executing (default): SELECT `id`, `email`, `password`, `RestaurantId` FROM `Users` AS `User` WHERE `User`.`email` = '[email protected]' LIMIT 1;

...

This will work for the student's demo, but not longterm. Looking for a lifecycle method that will only run once, and that changing state is safe and won't cause infinite loop

My r dependencies look like

"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.6",
"react-router": "^3.0.5",
"react-router-dom": "^4.2.2",
"react-transform-hmr": "^1.0.4",
"redux": "^3.7.2",

These routes are looking like

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import { Router, Route, Link, IndexRoute, browserHistory } from "react-router";

import reducers from "./reducers";
import { loadConfig, getConfig } from "./config";
import Nav from "./Nav";
import LoginPage from "./containers/LoginPage";
import MapShowRestaurants from "./components/MapShowRestaurants";
import RestaurantRegistration from "./containers/RestaurantRegistration";


const createStoreWithMiddleware = applyMiddleware()(createStore);


getConfig.then((config) => {
    loadConfig(config);

    ReactDOM.render(
        (
            <Provider store={createStoreWithMiddleware(reducers)}>
                <Router history={browserHistory}>
                    <Route path="/" component={Nav}>
                        <IndexRoute component={MapShowRestaurants} />
                        <Route path="/login" component={LoginPage} />
                        <Route path="/registerRestaurant" component={RestaurantRegistration} />
                    </Route>
                </Router>
            </Provider>
        ), document.querySelector('.container'));
})
.catch((err) => {
    console.log(err);
})
like image 481
codyc4321 Avatar asked Sep 28 '17 01:09

codyc4321


People also ask

What is the purpose of push () and replace () methods of history?

What is the purpose of push() and replace() methods of history ? A history instance has two methods for navigation purpose. If you think of the history as an array of visited locations, push() will add a new location to the array and replace() will replace the current location in the array with the new one.

How do I use Browserhistory in react router?

React Router uses the history package, which builds on the browser history API to provide an interface to which we can use easily in React apps. location - (object) The current location. May have the following properties: pathname - (string) The path of the URL.

Does history Push re render?

history. push is updating url but not re rendering : r/reactjs.

How do I use .push history?

Using history.push() is another approach where we make use of the history props React Router provides while rendering a component. In other words, this works when the component is being rendered by React Router, bypassing the component as a Component prop to a Route.


1 Answers

don't put API calls in a component lifecycle.

create an action

function getUserInfo(payload) {
  // do axios
  return { 
      type: 'GET_USER_INFO',
      payload: result_of_your_api_call,
      error: false/true
  };
}

create reducer for this action and mutate state in reducer

after that, you need to matStateToProps and mapDispatchToProps

connect(mapStateToProps, mapDispatchToProps)(YourMagicComponent)

that connects your component and your redux

After that, you get a redux state in your component. For more information please read this http://redux.js.org/docs/basics/ExampleTodoList.html

Remember 1. when component initialized it hasn't any data in props except defaultProps. 2. componentDidMount doesn't know anything outside component (available only default props and props defined before component mounted) 3. if you need to do something in componentWillUpdate you need to define rules for update that's component

componentWillUpdate(nextProps) {
  this.setState(key: nextProps.key)
}

shouldComponentUpdate(nextProps, nextState) {
  // fix for your update
  if (this.state.key !== nextProps.key) {
     return true;
  }
}
like image 182
Daniel Rosenberg Avatar answered Sep 18 '22 07:09

Daniel Rosenberg