Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component State fails to reset on route change following AWS Amplify withAuthenticator implementation #5046

Closed
4 tasks done
skims-ella-travelstead opened this issue Mar 6, 2024 · 3 comments
Labels
question General question

Comments

@skims-ella-travelstead
Copy link

Before creating a new issue, please confirm:

On which framework/platform are you having an issue?

React

Which UI component?

Authenticator

How is your app built?

Create React App (with Typescript)

What browsers are you seeing the problem on?

Chrome

Which region are you seeing the problem in?

us-east-2

Please describe your bug.

Hello,

We are in the middle of integrating AWS Cognito to our React App and encountered an issue where component state does not seem to reset after navigating to a route.

The app is created with:

Create React App (with Typescript)
aws-amplify/ui-react": "^6.1.5"
"aws-amplify": "^6.0.19"
"react": "^18.2.0"
"react-router-dom": "^6.22.2"

What's the expected behaviour?

The component state resets when navigating to the route.

Help us reproduce the bug!

Steps to reproduce:

User signs in using AWS Cognito + Amplify UI (withAuthenticator component)
User search for a keyword, this is where states dont seem to reset
It works fine when you enter the keyword in the URL bar (refreshes page), but not when entering the keyword on the search bar.

Code Snippet

App.tsx

Amplify.configure(config)

export function App({ user }: WithAuthenticatorProps) {
  return (
    <BrowserRouter>
      <Routes>
        <Route
          element={
            <>
              <NavBar user={user} />
              <div style={{ position: 'relative', minHeight: `calc(100vh - 135px)` }}>
                <Outlet />
              </div>
            </>
          }
        >
          <Route path='/' element={<Home />} />
          <Route
            path='/my-page/:mySearch'
            element={<MyPage key={window.location.pathname} />}
          />
        </Route>
      </Routes>
    </BrowserRouter>
  )
}

export default withAuthenticator(App, { hideSignUp: true })

Navbar.tsx

/* eslint-disable @typescript-eslint/no-unused-vars */
import {
  AppBar,
  Container,
  Toolbar,
} from '@mui/material'


import Search from './Search'


function NavBar(user: any) {
  return (
    <AppBar
      position='static'
      sx={{
        position: 'sticky',
        top: 0,
        width: '100%',
        zIndex: 2,
      }}
    >
      <Container maxWidth='xl'>
        <Toolbar disableGutters>

          <Search />

        </Toolbar>
      </Container>
    </AppBar>
  )
}

export default NavBar

Search.tsx

const Search = () => {
  const navigate = useNavigate()

  const handleKeyDown = (event: any) => {
    if (event.key === 'Enter') {
      navigate(`/my-page/${event.target.value}`)
    }
  }

  return (
    <Search>
      <SearchIconWrapper>
        <SearchIcon />
      </SearchIconWrapper>
      <StyledInputBase
        autoComplete='off'
        placeholder='Search'
        inputProps={{ 'aria-label': 'search' }}
        id='search'
        onKeyDown={handleKeyDown}
      />
    </Search>
  )
}

export default Search

MyPage.tsx

/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useEffect } from 'react'
import { useLocation, useParams } from 'react-router-dom'
import { Box} from '@mui/material'


import Loading from './Loading'

const MyPage = () => {
  const location = useLocation()
  const [showLoading, setLoading] = useState(true) // this state is not resetting to true
  console.log(showLoading, 'showLoading')

  useEffect(() => {
    console.log('MyPage useEffect')
    setTimeout(() => {
        setLoading(false)
        }, 5000)
  }, [location.pathname])



  return (
    <Box
      sx={{
        p: 4,
        marginLeft: '-32px',
        paddingBottom: '120px',
      }}
    >
      {showLoading && <Loading />}
      {!showLoading && (
        <p>not loading</p>
      )}


    </Box>
  )
}

export default MyPage

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: <https://bit.ly/CRA-vitals>
reportWebVitals();

Console log output

No response

Additional information and screenshots

No response

@github-actions github-actions bot added the pending-triage Issue is pending triage label Mar 6, 2024
@reesscot
Copy link
Contributor

reesscot commented Mar 7, 2024

Hi @skims-ella-travelstead,
The withAuthenticator is not really intended to be used with BrowserRouter in that way. I would recommend rendering the Authenticator in a /login route and then redirect to that page on any page that requires auth. I put together an example with your basic code here:
https://github.com/reesscot/react-router-amplify-authenticator

Please let me know if this solves your issue.

@reesscot reesscot added question General question pending-response and removed pending-triage Issue is pending triage labels Mar 7, 2024
@skims-ella-travelstead
Copy link
Author

Hi @skims-ella-travelstead, The withAuthenticator is not really intended to be used with BrowserRouter in that way. I would recommend rendering the Authenticator in a /login route and then redirect to that page on any page that requires auth. I put together an example with your basic code here: https://github.com/reesscot/react-router-amplify-authenticator

Please let me know if this solves your issue.

Hi @reesscot Thank you so much for the response! I want to preface that I am fairly new to React in general so apologize if i get some of this confused.

I tried the example code on the repo you provided and the problem is when I nest SearchBar like so (not sure if im nesting correctly here), but basically, I want to keep the search bar visible not just for the first search keyword.

App.tsx (added Outlet)

export default function App() {
  const navigate = useNavigate();

  return (
    <Flex gap="20px">
      <OrderSearch />
 

      <Button onClick={async () => {
        await signOut();
        navigate("/");
      }
      }>Sign out</Button>
      <Outlet />

    </Flex>
  )
}

main.tsx

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    loader: checkForUser,
    children: [
      {
        path: "/my-page/:mySearch",
        element: <MyPage key={window.location.pathname} />,
        loader: checkForUser,
      }
    ]
  },
  {
    path: "/login",
    element: <Login />,
  },
]);

The state don't refresh for MyPage. I did figure this out by using useLocation instead of window.location.pathname.

My initial App.tsx now looks like this (no longer using withAuthenticator) -- This works, it resets the state of MyPage component.

function PrivateRouter() {
  const location = useLocation()
  return (
    <Routes>
      <Route
        element={
          <>
            <NavBar />
            <div style={{ position: 'relative', minHeight: `calc(100vh - 135px)` }}>
              <Outlet />
            </div>
            <Footer />
          </>
        }
      >
        <Route path='/' element={<Home />} />
         <Route
            path='/my-page/:mySearch'
            element={<MyPage key={location.pathname} />}
          />
      </Route>
    </Routes>
  )
}

function PrivateRouting() {
  const { route } = useAuthenticator(context => [context.route]);

  // Use the value of route to decide which page to render
  return route === 'authenticated' ? <PrivateRouter /> : <Authenticator components={components} formFields={formFields} hideSignUp={true}/>;
}

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

export default App

The rest of the files stayed the same. But I am not sure if that is also advisable set-up. If this is out scope of Amplify, I totally understand and we can close this.

@reesscot
Copy link
Contributor

reesscot commented Mar 8, 2024

@skims-ella-travelstead I think the way you are doing it now by conditionally rendering your PrivateRouter based on authentication status makes sense. I'll close this out now since it seems like your question was answered. Please let us know if you have any further issues!

@reesscot reesscot closed this as completed Mar 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question General question
Projects
None yet
Development

No branches or pull requests

2 participants