0

I have in my parent component App.js a Header and a Footer.
I want to switch the components between the Header and Footer by clicking buttons inside the two of them.
Each time the pageIndex changed I want to replace to some other component.

I'm getting this error Cannot read property 'push' of undefined
Why am I getting this error and is that a clean and proper whey to do it (with my defaultRouter() and useMemo) ?

 function App() {
      const [pageIndex, setPageIndex] = useState();
      const [searchResults, setSearchResults] = useState([]);

      const history = useHistory();

       useEffect(() => {
         console.log(pageIndex, 'App');
       }, [pageIndex]);
    
    const defaultRouter = useMemo(() => {
     // Or -  `const DefaultRouter = useMemo(() =>` instead of `useEffect()`
    switch (pageIndex) {
      case 1:
        history.push({ pathname: '/search', state: searchResults });
        break;
      case 2:
        history.push('/cart');
        break;
      default:
        history.push('/');
        break;
    }
   }, [pageIndex]);

      return (
        <div className='App'>
          <Header/>
          <Home />
          {defaultRouter};  
          <Footer />  
          <BrowserRouter>
            <Route path='/'>
              <Home />
            </Route>
            <Route path='/search'>
              <Search
                searchResults={searchResults}
                setItemsAmount={setItemsAmount}
              />
            </Route>
            <Route path='/cart'>
              <Cart />
            </Route>
            <Route component={PageNotFound} />
          </BrowserRouter>
          ;
        </div>
      );
    }

Edit

As Shyam adviced in his answer below, I changed my App.js with useCallback but useCallback just doesn't work.

  const defaultRouter = useCallback(() => {
    console.log(pageIndex, 'callBack');
    switch (pageIndex) {
      case 1:
        history.push({ pathname: '/search', state: searchResults });
        break;
      case 2:
        history.push('/cart');
        break;
      default:
        history.push('/');
        break;
    }
  }, [pageIndex]);

Thanks

ExtraSun
  • 528
  • 2
  • 11
  • 31

1 Answers1

0

It's because the react-router context isn't set in that component. Since its the component that sets the context you could use useHistory in a sub-component, but not in that one.

Here is a very basic strategy for solving this issue:

const AppWrapper = () => {
  return (
    <Router> // Set context
      <App /> // Now App has access to context
    </Router>
  )
}

const App = () => {
  let history = useHistory(); // Works!
...

// Render routes in this component Then just be sure to use the "wrapper" component instead of App directly.

Answered here: https://stackoverflow.com/a/58221867/1723410

function App() {
  return (
    <BrowserRouter>
       <AppChildren />
    </BrowserRouter>
  )
}     

function AppChildren() {
      const [pageIndex, setPageIndex] = useState();
      const [searchResults, setSearchResults] = useState([]);

      const history = useHistory();

       useEffect(() => {
         console.log(pageIndex, 'App');
       }, [pageIndex]);
    
    const defaultRouter = useMemo(() => {
     // Or -  `const DefaultRouter = useMemo(() =>` instead of `useEffect()`
    switch (pageIndex) {
      case 1:
        history.push({ pathname: '/search', state: searchResults });
        break;
      case 2:
        history.push('/cart');
        break;
      default:
        history.push('/');
        break;
    }
   }, [pageIndex]);

      return (
        <div className='App'>
          <Header/>
          <Home />
          {defaultRouter};  
          <Footer />  
          <>
            <Route path='/'>
              <Home />
            </Route>
            <Route path='/search'>
              <Search
                searchResults={searchResults}
                setItemsAmount={setItemsAmount}
              />
            </Route>
            <Route path='/cart'>
              <Cart />
            </Route>
            <Route component={PageNotFound} />
          </>
        </div>
      );
} 
Shyam
  • 1,364
  • 7
  • 13
  • 1) Should `AppWrapper` be in a different jsx file and be exported ? 2) My now `useCallback ` don't work when updating `pageIndex` in the `Header` component. – ExtraSun Jul 11 '21 at 11:28
  • I tried it, it just doesn't work, nor with `useCallback`, `useMemo` or `useEffect` – ExtraSun Jul 11 '21 at 11:51
  • How/where to use the `AppWrapper` ? I tried in index.js instead of `` but it gives an error - `TypeError: Cannot read property 'location' of undefined` – ExtraSun Jul 11 '21 at 12:06
  • 1
    so here your BrowserRouter must be in parent component. I updated my answer above – Shyam Jul 11 '21 at 23:39