'SolidJS Router useNavigate() throws: Error: Make sure your app is wrapped in a <Router />
Even though my App is wrapped in a Router tag, when I use useNavigate()
or the <Navigate />
element I get the same error login.tsx:27 Error: Make sure your app is wrapped in a <Router />
Here is my index.tsx
/* @refresh reload */
import { render } from 'solid-js/web';
import './index.css';
import App from './App';
import { Router } from 'solid-app-router';
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById('root') as HTMLElement
)
App.tsx
const App: Component = () => {
return (
<>
<Routes>
<Route path="/" component={Landing} />
<Route path="/dashboard" component={Dashboard} />
</Routes>
</>
)
}
the landing component that is the parent of the Login component
import { useNavigate } from 'solid-app-router'
import { Component, createSignal, Show, onMount } from 'solid-js'
import { getIsValidSession } from '../services/session'
import '../styling/landing.css'
import CreateAccount from './landing/create-account'
import Login from './landing/login'
const Landing: Component = () => {
const [displayLogin, setDisplayLogin] = createSignal(true)
return (
<div class="landing text-center">
<main class="form-signin">
<img
class="mb-4"
src="src/assets/original/tracker-image.png"
alt=""
width="72"
height="85"
/>
<Show when={displayLogin()} fallback={<CreateAccount />}>
<Login />
</Show>
<Show
when={displayLogin()}
fallback={
<button
class="w-100 btn btn-lg btn-primary mt-3"
onClick={toggleDisplay}>
Login Existing User
</button>
}>
<button
class="w-100 btn btn-lg btn-primary mt-3"
onClick={toggleDisplay}>
Create Account
</button>
</Show>
</main>
</div>
)
}
export default Landing
and the Login component
import { useNavigate } from 'solid-app-router'
import { Component, createSignal } from 'solid-js'
import Response from '../../models/response'
import { PostSession, Session } from '../../models/session'
import * as session from '../../services/session'
const Login: Component = () => {
const [email, setEmail] = createSignal('')
const [password, setPassword] = createSignal('')
const login = async (event: any) => {
event.preventDefault()
try {
console.log(`Login: ${email()}, Password: ${password()}`)
const res = await session.login({
email: email(),
password: password(),
} as PostSession)
if (res.status !== 200) {
window.alert('Login failed')
} else {
const navigate = useNavigate()
navigate('/dashboard', { replace: true })
}
} catch (error) {
console.error(error)
}
}
return (
<form>
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input
type="email"
class="form-control"
id="floatingInput"
placeholder="[email protected]"
value={email()}
onChange={(e: any) => setEmail(e.target.value)}
/>
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating">
<input
type="password"
class="form-control"
id="floatingPassword"
placeholder="Password"
value={password()}
onChange={(e: any) => setPassword(e.target.value)}
/>
<label for="floatingPassword">Password</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" /> Remember me
</label>
</div>
<button
class="w-100 btn btn-lg btn-primary"
onClick={e => login(e)}>
Sign in
</button>
</form>
)
}
export default Login
What is the correct way to setup the Router for my use case?
Solution 1:[1]
The problem here (I think) is that you are using useNavigate()
in a event handler. The function needs to be called synchronously during render "within the tree", rather than in a callback, which doesn't have the same execution context. Try this:
const [email, setEmail] = createSignal('')
const [password, setPassword] = createSignal('')
const navigate = useNavigate()
and then use it in your event handler as you were:
} else {
navigate('/dashboard', { replace: true })
}
Does that work?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | FoolsWisdom |