0

I have a SPA that has multiple routes. Each of these routes are parent components to 2 children (a form component and a table component, that displays data fetched from an API). The goal is to be able to come back to the form with the fields populated as before if a user navigates away from it.

Here's what I have so far. My index.js:

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);

App.js

return (
            <Switch>
                <Route exact path="/" component={Landing} />
                <Route path="/onDemandProductSearch" component={OnDemandProductSearch} />
            </Switch>
        );

OnDemandProductSearch.js component has 2 children as mentioned above. I only display the form and once parameters on the form are selected, display the table with the fetched data.

class OnDemandProductSearch extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            products: [],
            isSearched: false
        };
    };

    onFiltersSelected = (products, queryParams) => {
        this.setState({
            products: products,
            isSearched: true
        });
        
        this.props.history.push({
            search: queryParams
        });
    };

    render() {
        return (
            <div>
                <Header />
                {this.props.history.location.search === "" ?
                    <OnDemandProductSearchMenu onFiltersSelected={this.onFiltersSelected} /> :
                    <InstanceList products={this.state.products} />}
            </div>
        );
    };
};

OnDemandProductSearchMenu.js,

fetch(`${apiURL}/simple-products/search`, {
            // body here
            })
        })
            .then((response) => {
                // more logic here 
            })
            .then((data) => {
                // I build a parameter string here I want the table component to be rendered with. This is important to the app
                var paramString = `.....`; 

                this.props.onFiltersSelected(data, paramString);
            })
            .catch((error) => {
                console.log(error);
            });

I read the docs for react-router, but wasn't clear on how the history works exactly. I understand a route needs to be pushed to the history, but how do I tackle it since the form component won't have access to history?

ameyaraje
  • 77
  • 1
  • 11

1 Answers1

0

There are a few different ways to get access to history...

Here's what you can do:

  1. Create a file called history.js in the src and put this code in there:

    import { createBrowserHistory } from 'history';
    
    export default createBrowserHistory();
    
  2. import Router not BrowserRoouter in your index.js and use that instead. Also import history from './history' and pass it as a prop to Router like so:

    import { Router } from 'react-router-dom'; // not BroswerRouter
    
        ReactDOM.render(
          <Router history={history}>
            <App />
          </Router>,
          document.getElementById('root')
        );
    

Now you have history available to you anywhere you want. Just import history into any component and use history.push()

For example in your OnDemandProductSearch just import history

import history from './history';

class OnDemandProductSearch extends React.Component {
    {......}
    
    onFiltersSelected = (products, queryParams) => {
        this.setState({
            products: products,
            isSearched: true
        });
        
        history.push({
            search: queryParams
        });
    };

    {......}
};
SakoBu
  • 3,448
  • 1
  • 11
  • 30
  • So I understand you're asking me to use `Router` instead of `BrowserRouter` and control `history` as a prop. But the code above is breaking something. It doesnt render anything. The URL changes, but the `OnDemandProductSearch` component doesn't show up. I tried a log in the `componentDidMount` – ameyaraje Jul 08 '20 at 21:04
  • What does the URL change to? – SakoBu Jul 08 '20 at 21:21
  • I have a `Header` component that links to `OnDemandProductSearch`. When I click it, the route changes to `/onDemandProductSearch` (as defined in my `App`). But nothing renders. Basically, I see the problem described in https://stackoverflow.com/questions/56707885/browserrouter-vs-router-with-history-push despite using `Router`. I cannot navigate from within the app, but it will render if I enter it in the browser directly. – ameyaraje Jul 08 '20 at 21:25
  • I'd need to see more code in order to debug... Sounds more like a structural / architecture issue... If you need access to the history API what I mentioned above is a valid way of doing it... – SakoBu Jul 08 '20 at 21:36
  • https://codesandbox.io/live/Q5RA7 You can use this to view the structure. Of course, I've removed some parts, but I've kept all code that I think relevant in there – ameyaraje Jul 08 '20 at 22:01
  • There was a minor typo but everything seems to work as expected: https://codesandbox.io/s/polished-paper-48hip – SakoBu Jul 09 '20 at 00:04
  • I diffed all files, didn't find any other than the `OnDemandProductSearchForm` being called `OnDemandProductSearchMenu`. This same code in my app doesn't work as expected. If I start with the landing, on clicking `/onDemandProductSearch`, the header disappears. If I load this route in the browser, navigating to `/` makes everything un-render. – ameyaraje Jul 09 '20 at 11:21