Skip to content

Commit

Permalink
Custom context feature
Browse files Browse the repository at this point in the history
  • Loading branch information
AlemTuzlak committed Nov 17, 2024
1 parent d9b0f9a commit 64dfbc1
Show file tree
Hide file tree
Showing 25 changed files with 1,063 additions and 86 deletions.
10 changes: 8 additions & 2 deletions docs/posts/main/configuration/general.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ will be automatically imported and added to the dev tools.
## `includeInProd`
This option is used to set whether the plugin should be included in production builds or not.
This option is used to set whether the plugin should be included in production builds or not.
By default it is set to `undefined` and if you set this option to an object with the `client` and `server` properties set to `true` the plugin will be included in production builds.
By default it is set to `undefined` and if you set this option to an object with the `client`, `context` and `server` properties set to `true` the plugin will be included in production builds.
The client part includes the dev tools with the plugin and the server part includes the info logs. You can granularly configure the
exact behavior of both sides with client and server configs respectively.
Each of these flags will include a part of the plugin in production, in order for any of these to work `react-router-devtools` need to be switched over to
a regular dependency and included in your project. If you only want to include the `devTools` helper in production, for example, you can
set `includeInProd` to `{ devTools: true }` and the `devTools` part will be included in production and available always.
<Warn title="Be careful!">
If you decide to deploy parts to production you should be very careful that you don't expose the dev tools to your clients or anybody
who is not supposed to see them. Also the server part uses chalk which might not work in non-node environments!
Expand All @@ -51,6 +56,7 @@ exact behavior of both sides with client and server configs respectively.
includeInProd: {
client: true,
server: true,
devTools: true
},
}),
],
Expand Down
198 changes: 198 additions & 0 deletions docs/posts/main/features/devtools.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
---
title: "Devtools context"
alternateTitle: "Devtools context"
description: "Using the devtools context to trace events and send them to the network tab"
---

import Info from "./info.tsx";
import Warn from "./warn.tsx";

## Devtools extended context

The devtools context is a set of utilities that you can use in your data fetching functions to trace events
in the network tab of react-router-devtools. You can also include them in your production builds if you do not want
the hassle of having to optionally check if they are defined.

The general usage of the devtools context is as follows:

```ts
// The devTools object is available in all data fetching functions
export const loader = async ({ request, devTools }: LoaderFunctionArgs) => {
const tracing = devTools?.tracing;
// tracing is a set of utilities to be used in your data fetching functions to trace events
// in network tab of react-router-devtools
const startTime = tracing.start("my-event")
// do something here, eg DB call
tracing.end("my-event", startTime!)
return "data"
}
```

You can also use the devtools context in your action functions:

```ts
export const action = async ({ request, devTools }: ActionFunctionArgs) => {
const tracing = devTools?.tracing;
// tracing is a set of utilities to be used in your data fetching functions to trace events
// in network tab of react-router-devtools
const startTime = tracing?.start("my-event")
// do something
tracing?.end("my-event", startTime!)
return "data"
}
```

The devtools context is also available in your client loader and client action functions:

```ts
export const clientLoader = async ({ request, devTools }: ClientLoaderFunctionArgs) => {
const tracing = devTools?.tracing;
// tracing is a set of utilities to be used in your data fetching functions to trace events
// in network tab of react-router-devtools
const startTime = tracing?.start("my-event")
// do something
tracing?.end("my-event", startTime!)
return "data"
}
```

```ts
export const clientAction = async ({ request, devTools }: ClientActionFunctionArgs) => {
const tracing = devTools?.tracing;
// tracing is a set of utilities to be used in your data fetching functions to trace events
// in network tab of react-router-devtools
const startTime = tracing?.start("my-event")
// do something
tracing?.end("my-event", startTime!)
return "data"
}
```


<Info>
If you want to make the devTools available always in your project, you can set `includeInProd` to `{ devTools: true }` in your vite config.

In production the trace calls won't do anything, but the tracing will be more convinient to use.

If you do so you can also override the types by adding the following to your project:
```ts
import type { ExtendedContext } from "react-router-devtools/context";

declare module "react-router" {
interface LoaderFunctionArgs {
devTools: ExtendedContext
}
interface ActionFunctionArgs {
devTools: ExtendedContext
}
}
```
</Info>

## RouteId

The routeId is a string that is used to identify the route that is being processed. You can access it like so:
```ts
const loader = async ({ request, devTools }: LoaderFunctionArgs) => {
const routeId = devTools?.routeId;
// do something with the routeId
return "data"
}
```

## Tracing

The tracing object contains all the utilities related to network tab tracing feature of react-router-devtools.


There are three functions you can use:
- trace
- start
- end



### trace

The `trace` function is a function that will trace the event given to it, pipe it to the network tab of react-router-devtools and show you analytics.

This works by calling the provided function and analyzing the time it takes to execute it.

```ts
const loader = async ({ request, devTools }: LoaderFunctionArgs) => {
const tracing = devTools?.tracing;
// this will be traced in the network tab of react-router-devtools
const user = tracing?.trace("my-event",() => getUser())

return { user }
}
```

#### Parameters

- `name` - The name of the event
- `event` - The event to be traced

#### Returns

The result of the event

### start

The `start` function is a function that will start a trace for the name provided to it and return the start time.
This is used together with `end` to trace the time of the event.

```ts
export const loader = async ({ request, devTools }: LoaderFunctionArgs) => {
const tracing = devTools?.tracing;
// this will be traced in the network tab of react-router-devtools
const startTime = tracing?.start("my-event")
// do something here, eg DB call

// End the trace
tracing?.end("my-event", startTime!)
return "data"
}
```

<Warn title="Warning">
This function relies on you using the `end` with the same name as the start event, otherwise
you will end up having a never ending loading bar in the network tab!
</Warn>


#### Parameters

- `name` - The name of the event

#### Returns

The start time of the event

### end

The `end` function is a function that will end a trace for the name provided to it and return the end time.

```ts
export const loader = async ({ request, devTools }: LoaderFunctionArgs) => {
const tracing = devTools?.tracing;
// this will be traced in the network tab of react-router-devtools
const startTime = tracing?.start("get user")
// do something here, eg DB call
const user = await getUser();
// End the trace
tracing?.end("get user", startTime!, { user })
return "data"

}
```

#### Parameters

- `name` - The name of the event
- `startTime` - The start time of the sendEvent
- `data` - The data to be sent with the event

#### Returns

The data provided in the last parameter
9 changes: 9 additions & 0 deletions docs/posts/main/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"server": "configuration/server",
"shortcuts": "features/shortcuts",
"active-page-tab": "features/active-page-tab",
"devtools": "features/devtools",
"routes-tab": "features/routes-tab",
"network-tab": "features/network-tab",
"errors-tab": "features/errors-tab",
Expand Down Expand Up @@ -72,6 +73,14 @@
"slug": "shortcuts",
"spacer": true
},
"devtools": {
"title": "Development Tools context",
"alternateTitle": "Development Tools context",
"description": "Detailed overview of all the features offered on the Dev Tools context.",
"section": "Features",
"slug": "devtools",
"spacer": true
},
"active-page-tab": {
"title": "Active Page Tab",
"alternateTitle": "Active Page Tab",
Expand Down
16 changes: 13 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "react-router-devtools",
"description": "Devtools for React Router - debug and 10x your DX with React Router",
"description": "Devtools for React Router - debug, trace, find hydration errors, catch bugs and inspect server/client data with react-router-devtools",
"author": "Alem Tuzlak",
"version": "2.0.0",
"version": "1.0.1",
"license": "MIT",
"keywords": [
"react-router",
Expand Down Expand Up @@ -39,6 +39,14 @@
"types": "./dist/client.d.ts",
"default": "./dist/client.js"
},
"./context": {
"import": {
"types": "./dist/context.d.ts",
"default": "./dist/context.js"
},
"types": "./dist/context.d.ts",
"default": "./dist/context.js"
},
"./server": {
"import": {
"types": "./dist/server.d.ts",
Expand Down Expand Up @@ -70,11 +78,13 @@
"runner": "npm-run-all -s build -p watch-all",
"dev": "npm run runner react-router-vite",
"build": "run-s tsup:* -- --clean",
"watch-all": "npm-run-all -p tsup:index:watch tsup:client:watch tsup:server:watch -- --watch",
"watch-all": "npm-run-all -p tsup:index:watch tsup:client:watch tsup:server:watch tsup:context:watch -- --watch",
"tsup:index": "tsup",
"tsup:index:watch": "tsup --watch",
"tsup:client": "tsup --config tsup-client.config.ts",
"tsup:context": "tsup --config tsup-context.config.ts",
"tsup:server": "tsup --config tsup-server.config.ts",
"tsup:context:watch": "npm run tsup:context -- --watch",
"tsup:client:watch": "npm run tsup:client -- --watch",
"tsup:server:watch": "npm run tsup:server -- --watch",
"check": "biome check .",
Expand Down
9 changes: 5 additions & 4 deletions src/client/components/network-tracer/NetworkBar.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { animate, motion, useMotionValue } from "framer-motion"
import type React from "react"
import { useEffect } from "react"
import type { NetworkRequest } from "./types"
import type { RequestEvent } from "../../../shared/request-event"

interface NetworkBarProps {
request: NetworkRequest
request: RequestEvent
index: number
minTime: number
pixelsPerMs: number
barHeight: number
barPadding: number
now: number
onClick: (e: React.MouseEvent, request: NetworkRequest, order: number) => void
onClick: (e: React.MouseEvent, request: RequestEvent, order: number) => void
isActive: boolean
}

Expand All @@ -20,6 +20,7 @@ const COLORS = {
"client-loader": "#60a5fa",
action: "#f59e0b",
"client-action": "#ef4444",
"custom-event": "#ffffff",
pending: "#94a3b8",
error: "#dc2626",
}
Expand Down Expand Up @@ -90,7 +91,7 @@ export const NetworkBar: React.FC<NetworkBarProps> = ({
className="relative overflow-hidden group cursor-pointer hover:brightness-110"
onClick={(e) => onClick(e, request, index)}
>
{request.state === "pending" && isActive && (
{isActive && (
<motion.div
className="absolute inset-0 bg-gradient-to-r from-transparent via-white to-transparent opacity-20"
animate={{ x: ["-100%", "100%"] }}
Expand Down
12 changes: 7 additions & 5 deletions src/client/components/network-tracer/NetworkWaterfall.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import type React from "react"
import { useEffect, useRef, useState } from "react"
import { useHotkeys } from "react-hotkeys-hook"
import { Tooltip } from "react-tooltip"
import type { RequestEvent } from "../../../shared/request-event"
import { METHOD_COLORS } from "../../tabs/TimelineTab"
import { Tag } from "../Tag"
import { NetworkBar } from "./NetworkBar"
import { REQUEST_BORDER_COLORS, RequestDetails } from "./RequestDetails"
import type { NetworkRequest, Position } from "./types"

interface Props {
requests: NetworkRequest[]
requests: RequestEvent[]
width: number
}

Expand All @@ -22,17 +22,19 @@ const MAX_SCALE = 10
const FUTURE_BUFFER = 1000 // 2 seconds ahead
const INACTIVE_THRESHOLD = 100 // 1 seconds

export const TYPE_COLORS = {
const TYPE_COLORS = {
loader: "bg-green-500",
"client-loader": "bg-blue-500",
action: "bg-yellow-500",
"client-action": "bg-purple-500",
"custom-event": "bg-white",
}
const TYPE_TEXT_COLORS = {
loader: "text-green-500",
"client-loader": "text-blue-500",
action: "text-yellow-500",
"client-action": "text-purple-500",
"custom-event": "text-white",
}

const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
Expand Down Expand Up @@ -104,7 +106,7 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {
// setScale((s) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, s + delta)))
// }

const handleBarClick = (e: React.MouseEvent, request: NetworkRequest, index: number) => {
const handleBarClick = (e: React.MouseEvent, request: RequestEvent, index: number) => {
setSelectedRequest(index)
}

Expand Down Expand Up @@ -162,7 +164,7 @@ const NetworkWaterfall: React.FC<Props> = ({ requests, width }) => {

<Tooltip place="top" id={`${request.id}${request.startTime}`} />
<div className="pr-4">
<div>{request.id}</div>
<div className="whitespace-nowrap">{request.id}</div>
</div>
</button>
<div className="flex items-center ml-auto">
Expand Down
Loading

0 comments on commit 64dfbc1

Please sign in to comment.