or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-routing.mdhistory.mdhooks.mdindex.mdlocation-context.mdnavigation.mdrouting.mdserver-rendering.mdutilities.md

history.mddocs/

0

# History Management

1

2

History utilities for creating custom history instances and managing navigation state in different environments.

3

4

## Capabilities

5

6

### Global History

7

8

Default history instance that uses browser history or memory fallback for navigation state management.

9

10

```javascript { .api }

11

/**

12

* Default history instance using browser history or memory fallback

13

* Available as a singleton for the entire application

14

*/

15

const globalHistory: History;

16

17

interface History {

18

location: Location;

19

transitioning: boolean;

20

listen(listener: (event: HistoryEvent) => void): () => void;

21

navigate(to: string | number, options?: NavigateOptions): Promise<void>;

22

_onTransitionComplete(): void;

23

}

24

25

interface HistoryEvent {

26

location: Location;

27

action: "PUSH" | "POP";

28

}

29

```

30

31

**Usage Examples:**

32

33

```javascript

34

import { globalHistory } from "@reach/router";

35

36

// Access current location

37

console.log(globalHistory.location.pathname);

38

39

// Listen to navigation changes

40

const unlisten = globalHistory.listen(({ location, action }) => {

41

console.log(`Navigation ${action} to ${location.pathname}`);

42

});

43

44

// Clean up listener

45

unlisten();

46

47

// Direct navigation

48

globalHistory.navigate("/about");

49

50

// Check if navigation is in progress

51

if (globalHistory.transitioning) {

52

console.log("Navigation in progress...");

53

}

54

55

// Manual transition completion (rarely needed)

56

globalHistory._onTransitionComplete();

57

```

58

59

### Create History Function

60

61

Factory function for creating custom history instances with different location sources.

62

63

```javascript { .api }

64

/**

65

* Creates a custom history instance with specified source

66

* @param source - Location source (window or memory source)

67

* @param options - Additional configuration options

68

* @returns Custom history instance

69

*/

70

function createHistory(source: LocationSource, options?: any): History;

71

72

interface LocationSource {

73

location: {

74

pathname: string;

75

search: string;

76

hash?: string;

77

href?: string;

78

origin?: string;

79

protocol?: string;

80

host?: string;

81

hostname?: string;

82

port?: string;

83

};

84

history: {

85

state: any;

86

pushState(state: any, title: string | null, url: string): void;

87

replaceState(state: any, title: string | null, url: string): void;

88

go(delta: number): void;

89

};

90

addEventListener(type: string, listener: () => void): void;

91

removeEventListener(type: string, listener: () => void): void;

92

}

93

```

94

95

**Usage Examples:**

96

97

```javascript

98

import { createHistory, createMemorySource } from "@reach/router";

99

100

// Create history with memory source for testing

101

const testHistory = createHistory(createMemorySource("/initial-path"));

102

103

// Create history with custom window-like source

104

const customSource = {

105

location: { pathname: "/custom", search: "" },

106

history: {

107

state: null,

108

pushState: (state, title, url) => console.log("Push:", url),

109

replaceState: (state, title, url) => console.log("Replace:", url),

110

go: (delta) => console.log("Go:", delta)

111

},

112

addEventListener: (type, listener) => {},

113

removeEventListener: (type, listener) => {}

114

};

115

116

const customHistory = createHistory(customSource);

117

118

// Use custom history with LocationProvider

119

import { LocationProvider } from "@reach/router";

120

121

const TestApp = () => (

122

<LocationProvider history={testHistory}>

123

<App />

124

</LocationProvider>

125

);

126

127

// History for specific features

128

const createFeatureHistory = (initialPath) => {

129

const source = createMemorySource(initialPath);

130

return createHistory(source);

131

};

132

133

const modalHistory = createFeatureHistory("/modal/welcome");

134

const wizardHistory = createFeatureHistory("/wizard/step1");

135

```

136

137

### Create Memory Source Function

138

139

Creates an in-memory location source for testing or non-browser environments.

140

141

```javascript { .api }

142

/**

143

* Creates an in-memory location source for testing or non-browser environments

144

* @param initialPath - Starting path for the memory source

145

* @returns Memory source compatible with createHistory

146

*/

147

function createMemorySource(initialPath?: string): LocationSource;

148

149

interface MemorySource extends LocationSource {

150

history: {

151

entries: Array<{ pathname: string; search: string }>;

152

index: number;

153

state: any;

154

pushState(state: any, title: string | null, url: string): void;

155

replaceState(state: any, title: string | null, url: string): void;

156

go(delta: number): void;

157

};

158

}

159

```

160

161

**Usage Examples:**

162

163

```javascript

164

import { createMemorySource, createHistory } from "@reach/router";

165

166

// Basic memory source

167

const memorySource = createMemorySource("/home");

168

const memoryHistory = createHistory(memorySource);

169

170

// Memory source with query parameters

171

const sourceWithQuery = createMemorySource("/search?q=react&page=1");

172

173

// Testing navigation sequences

174

const testNavigationSequence = async () => {

175

const source = createMemorySource("/");

176

const history = createHistory(source);

177

178

// Navigate to different pages

179

await history.navigate("/about");

180

await history.navigate("/contact");

181

await history.navigate("/products/123");

182

183

// Check history entries

184

console.log(source.history.entries);

185

console.log("Current index:", source.history.index);

186

187

// Navigate back

188

history.navigate(-1);

189

console.log("After back:", history.location.pathname);

190

};

191

192

// Memory source for isolated testing

193

const createIsolatedTest = (initialPath) => {

194

const source = createMemorySource(initialPath);

195

const history = createHistory(source);

196

197

return {

198

history,

199

navigate: history.navigate,

200

getPath: () => history.location.pathname,

201

getEntries: () => source.history.entries,

202

getCurrentIndex: () => source.history.index

203

};

204

};

205

206

// Usage in tests

207

const test = createIsolatedTest("/app");

208

await test.navigate("/users/123");

209

await test.navigate("/users/123/edit");

210

test.navigate(-1); // Back to /users/123

211

console.log(test.getPath()); // "/users/123"

212

213

// React Native or server environments

214

const createServerHistory = (initialUrl) => {

215

const source = createMemorySource(initialUrl);

216

return createHistory(source);

217

};

218

219

const serverHistory = createServerHistory("/api/v1/users");

220

```

221

222

### Navigation Promise Handling

223

224

Understanding how navigation promises work and handling transition states.

225

226

```javascript { .api }

227

/**

228

* Navigation returns promises for handling async transitions

229

*/

230

interface NavigationPromise extends Promise<void> {

231

// Resolves when navigation and focus management complete

232

// Useful for waiting on navigation in tests or complex flows

233

}

234

```

235

236

**Usage Examples:**

237

238

```javascript

239

import { navigate, globalHistory } from "@reach/router";

240

241

// Wait for navigation completion

242

const handleLogin = async (credentials) => {

243

const user = await login(credentials);

244

if (user) {

245

// Wait for navigation to complete before showing success message

246

await navigate("/dashboard");

247

showSuccessMessage("Welcome back!");

248

}

249

};

250

251

// Sequential navigation

252

const wizardFlow = async () => {

253

await navigate("/wizard/step1");

254

// Wait for user input...

255

await navigate("/wizard/step2");

256

// Wait for user input...

257

await navigate("/wizard/complete");

258

};

259

260

// Navigation with loading states

261

const NavigationButton = ({ to, children }) => {

262

const [navigating, setNavigating] = React.useState(false);

263

264

const handleClick = async () => {

265

setNavigating(true);

266

try {

267

await navigate(to);

268

} finally {

269

setNavigating(false);

270

}

271

};

272

273

return (

274

<button onClick={handleClick} disabled={navigating}>

275

{navigating ? "Navigating..." : children}

276

</button>

277

);

278

};

279

280

// Transition monitoring

281

const TransitionMonitor = () => {

282

const [isTransitioning, setIsTransitioning] = React.useState(false);

283

284

React.useEffect(() => {

285

const checkTransition = () => {

286

setIsTransitioning(globalHistory.transitioning);

287

};

288

289

const unlisten = globalHistory.listen(checkTransition);

290

checkTransition(); // Initial check

291

292

return unlisten;

293

}, []);

294

295

return isTransitioning ? <div>Loading...</div> : null;

296

};

297

298

// Error handling during navigation

299

const safeNavigate = async (to, options = {}) => {

300

try {

301

await navigate(to, options);

302

} catch (error) {

303

console.error("Navigation failed:", error);

304

// Handle navigation errors

305

await navigate("/error", {

306

state: { originalDestination: to, error: error.message }

307

});

308

}

309

};

310

```

311

312

### History Integration Patterns

313

314

Common patterns for integrating history with application state and external systems.

315

316

**Usage Examples:**

317

318

```javascript

319

// History synchronization with external state

320

const createSyncedHistory = (externalStore) => {

321

const history = createHistory(createMemorySource("/"));

322

323

// Sync external state changes to history

324

externalStore.subscribe((state) => {

325

const newPath = state.currentRoute || "/";

326

if (history.location.pathname !== newPath) {

327

history.navigate(newPath, { replace: true });

328

}

329

});

330

331

// Sync history changes to external state

332

history.listen(({ location }) => {

333

externalStore.dispatch({

334

type: "ROUTE_CHANGED",

335

payload: location.pathname

336

});

337

});

338

339

return history;

340

};

341

342

// History with persistent state

343

const createPersistentHistory = (storageKey) => {

344

const initialPath = localStorage.getItem(storageKey) || "/";

345

const history = createHistory(createMemorySource(initialPath));

346

347

history.listen(({ location }) => {

348

localStorage.setItem(storageKey, location.pathname);

349

});

350

351

return history;

352

};

353

354

// Multi-frame history coordination

355

const createCoordinatedHistory = () => {

356

const history = createHistory(createMemorySource("/"));

357

358

// Listen for messages from other frames

359

window.addEventListener("message", (event) => {

360

if (event.data.type === "NAVIGATE") {

361

history.navigate(event.data.path);

362

}

363

});

364

365

// Broadcast navigation to other frames

366

history.listen(({ location }) => {

367

window.parent.postMessage({

368

type: "LOCATION_CHANGED",

369

path: location.pathname

370

}, "*");

371

});

372

373

return history;

374

};

375

376

// History with undo/redo functionality

377

const createUndoableHistory = () => {

378

const source = createMemorySource("/");

379

const history = createHistory(source);

380

381

const undoStack = [];

382

const redoStack = [];

383

384

const originalNavigate = history.navigate;

385

386

history.navigate = (to, options = {}) => {

387

if (typeof to === "string") {

388

undoStack.push(history.location.pathname);

389

redoStack.length = 0; // Clear redo stack

390

}

391

return originalNavigate(to, options);

392

};

393

394

history.undo = () => {

395

if (undoStack.length > 0) {

396

const previousPath = undoStack.pop();

397

redoStack.push(history.location.pathname);

398

return originalNavigate(previousPath, { replace: true });

399

}

400

};

401

402

history.redo = () => {

403

if (redoStack.length > 0) {

404

const nextPath = redoStack.pop();

405

undoStack.push(history.location.pathname);

406

return originalNavigate(nextPath, { replace: true });

407

}

408

};

409

410

return history;

411

};

412

```