mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Fix for #348 - migrate our NextJS project to a pure webpack project w/ a single bundle - [x] Switch from `next/link` to `react-router-dom`'s link > This part was easy - just change the import to `import { Link } from "react-router-dom"` and `<Link href={...} />` to `<Link to={...} />` - [x] Switch from `next/router` to `react-router-dom`'s paradigms (`useNavigation`, `useLocation`, and `useParams`) > `router.push` can be converted to `navigate(...)` (provided by the `useNavigate` hook) > `router.replace` can be converted `navigate(..., {replace: true})` > Query parameters (`const { query } = useRouter`) can be converted to `const query = useParams()`) - [x] Implement client-side routing with `react-router-dom` > Parameterized routes in NextJS like `projects/[organization]/[project]` would look like: > ``` > <Route path="projects"> > <Route path=":organization/:project"> > <Route index element={<ProjectPage />} /> > </Route> > </Route> > ``` I've hooked up a `build:analyze` command that spins up a server to show the bundle size: <img width="1303" alt="image" src="https://user-images.githubusercontent.com/88213859/157496889-87c5fdcd-fad1-4f2e-b7b6-437aebf99641.png"> The bundle looks OK, but there are some opportunities for improvement - the heavy-weight dependencies, like React, ReactDOM, Material-UI, and lodash could be brought in via a CDN: https://stackoverflow.com/questions/50645796/how-to-import-reactjs-material-ui-using-a-cdn-through-webpacks-externals
74 lines
1.8 KiB
TypeScript
74 lines
1.8 KiB
TypeScript
import { useLocation, useNavigate } from "react-router-dom"
|
|
import React, { useContext, useEffect } from "react"
|
|
import useSWR from "swr"
|
|
|
|
import * as API from "../api"
|
|
|
|
export interface User {
|
|
readonly id: string
|
|
readonly username: string
|
|
readonly email: string
|
|
readonly created_at: string
|
|
}
|
|
|
|
export interface UserContext {
|
|
readonly error?: Error
|
|
readonly me?: User
|
|
readonly signOut: () => Promise<void>
|
|
}
|
|
|
|
const UserContext = React.createContext<UserContext>({
|
|
signOut: () => {
|
|
return Promise.reject("Sign out API not available")
|
|
},
|
|
})
|
|
|
|
export const useUser = (redirectOnError = false): UserContext => {
|
|
const ctx = useContext(UserContext)
|
|
const navigate = useNavigate()
|
|
const { pathname } = useLocation()
|
|
|
|
const requestError = ctx.error
|
|
useEffect(() => {
|
|
if (redirectOnError && requestError) {
|
|
navigate({
|
|
pathname: "/login",
|
|
search: "?redirect=" + encodeURIComponent(pathname),
|
|
})
|
|
}
|
|
// Disabling exhaustive deps here because it can cause an
|
|
// infinite useEffect loop. Should (hopefully) go away
|
|
// when we switch to an alternate routing strategy.
|
|
}, [redirectOnError, requestError]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
return ctx
|
|
}
|
|
|
|
export const UserProvider: React.FC = (props) => {
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
const { data, error, mutate } = useSWR("/api/v2/users/me")
|
|
|
|
const signOut = async () => {
|
|
await API.logout()
|
|
// Tell SWR to invalidate the cache for the user endpoint
|
|
await mutate("/api/v2/users/me")
|
|
navigate({
|
|
pathname: "/login",
|
|
search: "?redirect=" + encodeURIComponent(location.pathname),
|
|
})
|
|
}
|
|
|
|
return (
|
|
<UserContext.Provider
|
|
value={{
|
|
error: error,
|
|
me: data,
|
|
signOut: signOut,
|
|
}}
|
|
>
|
|
{props.children}
|
|
</UserContext.Provider>
|
|
)
|
|
}
|