Tengo este código en el que uso react-router-dom v6 para enrutar
class App extends Component { constructor(props) { super(props); this.state = { accounts: [], }; } componentDidMount() { getAccounts().then(r => this.setState({ // async req accounts: r })); }; render() { return ( <Routes> <Route path="/" element={<AppLayout accounts={this.state.accounts} />}> <Route index element={<Navigate to={`/account/1`} />}/> <Route path="/account/:accountId" element={<TimelinePage/>}/> <Route path="*" element={<NotFoundPage/>}/> </Route> </Routes> ); } } Quiero redirigir desde la página de inicio a /accounts/${this.state.accounts[0]} pero la matriz this.state.accounts se completa después de una solicitud asincrónica antes de montar el componente de la aplicación y resulta que la primera vez que renderizar, llego a <NotFoundPage/> . Esto es lógico, pero ¿cómo puedo obtener datos asíncronos primero y redirigir solo después de eso?
Hice una solución temporal
<Route index element={<Navigate to={'/account/1'} />}/>`Pero esto no garantiza que siempre existirá un elemento con ID 1 en la matriz
PS Array this.state.accounts contiene objetos que tienen la propiedad numérica accountId . Dentro del componente <TimelinePage/> uso useParams() para obtener la identificación de la cuenta y en el <AppLayout/> represento cada elemento de la matriz
Use el useNavigate para acceder a la función de navigate y emita una redirección imperativa después de que se hayan obtenido las cuentas. Sin embargo, este problema aquí es que la App es un componente de clase y no puede usar directamente los ganchos React.
Las opciones son:
App en un componente de función.withRouter para acceder al gancho e inyecte navigate como accesorio. App es lo suficientemente simple como para que sea trivial convertirla en un componente de función.
import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; const App = () => { const navigate = useNavigate(); const [accounts, setAccounts] = useState([]); useEffect(() => { getAccounts() .then(accounts => { setAccounts(accounts); navigate("/account/1", { replace: true }); }); }, []); return ( <Routes> <Route path="/" element={<AppLayout accounts={accounts} />}> <Route path="/account/:accountId" element={<TimelinePage />} /> <Route path="*" element={<NotFoundPage />} /> </Route> </Routes> ); } Si los componentes de la clase son más complejos, puede ser más fácil crear un withRouter personalizado con el enrutador y pasar navigate como accesorio.
import { useNavigate, ...other hooks... } from 'react-router-dom'; const withRouter = Component => props => { const navigate = useNavigate(); ...other hooks... return ( <Component {...props} navigate={navigate} ...other hooks... /> ); };aplicación
class App extends Component { state = { accounts: [], } componentDidMount() { const { navigate } = this.props; getAccounts() .then(accounts => { this.setState({ accounts }); navigate("/account/1", { replace: true }); }); } render() { const { accounts } = this.state; return ( <Routes> <Route path="/" element={<AppLayout accounts={accounts} />}> <Route path="/account/:accountId" element={<TimelinePage />} /> <Route path="*" element={<NotFoundPage />} /> </Route> </Routes> ); } } export default withRouter(App);