or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-system.mdcontext-scoping.mdcontrol-flow.mdindex.mdreactive-primitives.mdresources-async.mdstore-management.mdweb-rendering.md

context-scoping.mddocs/

0

# Context and Scoping

1

2

Context API for passing data through the component tree and scoping utilities for managing reactive ownership and cleanup.

3

4

## Capabilities

5

6

### Context Creation and Usage

7

8

Create and consume context for passing data through component trees without prop drilling.

9

10

```typescript { .api }

11

/**

12

* Creates a Context to handle state scoped for component children

13

* @param defaultValue - Default value for the context

14

* @returns Context object with Provider component

15

*/

16

function createContext<T>(defaultValue?: T): Context<T>;

17

18

/**

19

* Uses a context to receive scoped state from parent's Context.Provider

20

* @param context - Context object to consume

21

* @returns Current context value

22

*/

23

function useContext<T>(context: Context<T>): T;

24

25

interface Context<T> {

26

id: symbol;

27

Provider: ContextProviderComponent<T>;

28

defaultValue: T;

29

}

30

31

type ContextProviderComponent<T> = FlowComponent<{ value: T }>;

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

import { createContext, useContext, createSignal, ParentComponent } from "solid-js";

38

39

// Create theme context

40

interface Theme {

41

primary: string;

42

secondary: string;

43

mode: "light" | "dark";

44

}

45

46

const ThemeContext = createContext<Theme>({

47

primary: "#007bff",

48

secondary: "#6c757d",

49

mode: "light"

50

});

51

52

// Theme provider component

53

const ThemeProvider: ParentComponent<{ theme: Theme }> = (props) => {

54

return (

55

<ThemeContext.Provider value={props.theme}>

56

{props.children}

57

</ThemeContext.Provider>

58

);

59

};

60

61

// Component that uses theme context

62

function ThemedButton(props: { children: JSX.Element; onClick?: () => void }) {

63

const theme = useContext(ThemeContext);

64

65

return (

66

<button

67

style={{

68

"background-color": theme.primary,

69

color: theme.mode === "dark" ? "white" : "black",

70

border: `1px solid ${theme.secondary}`

71

}}

72

onClick={props.onClick}

73

>

74

{props.children}

75

</button>

76

);

77

}

78

79

// App with theme context

80

function App() {

81

const [isDark, setIsDark] = createSignal(false);

82

83

const theme = () => ({

84

primary: isDark() ? "#bb86fc" : "#007bff",

85

secondary: isDark() ? "#03dac6" : "#6c757d",

86

mode: isDark() ? "dark" as const : "light" as const

87

});

88

89

return (

90

<ThemeProvider theme={theme()}>

91

<div style={{

92

"background-color": isDark() ? "#121212" : "#ffffff",

93

"min-height": "100vh",

94

padding: "20px"

95

}}>

96

<h1>Themed App</h1>

97

<ThemedButton onClick={() => setIsDark(!isDark())}>

98

Toggle {isDark() ? "Light" : "Dark"} Mode

99

</ThemedButton>

100

</div>

101

</ThemeProvider>

102

);

103

}

104

```

105

106

### Advanced Context Patterns

107

108

Create multiple contexts and nested providers for complex state management.

109

110

**Usage Examples:**

111

112

```typescript

113

import { createContext, useContext, createSignal, createMemo } from "solid-js";

114

115

// User context

116

interface User {

117

id: number;

118

name: string;

119

role: "admin" | "user";

120

}

121

122

const UserContext = createContext<User | null>(null);

123

124

// Settings context

125

interface Settings {

126

language: string;

127

notifications: boolean;

128

theme: "light" | "dark";

129

}

130

131

const SettingsContext = createContext<Settings>({

132

language: "en",

133

notifications: true,

134

theme: "light"

135

});

136

137

// Combined app context provider

138

function AppProviders(props: { children: JSX.Element }) {

139

const [user, setUser] = createSignal<User | null>(null);

140

const [settings, setSettings] = createSignal<Settings>({

141

language: "en",

142

notifications: true,

143

theme: "light"

144

});

145

146

// Login function available through context

147

const userActions = {

148

login: (userData: User) => setUser(userData),

149

logout: () => setUser(null),

150

updateSettings: (newSettings: Partial<Settings>) =>

151

setSettings(prev => ({ ...prev, ...newSettings }))

152

};

153

154

return (

155

<UserContext.Provider value={user()}>

156

<SettingsContext.Provider value={settings()}>

157

{/* We can also create an actions context */}

158

<ActionsContext.Provider value={userActions}>

159

{props.children}

160

</ActionsContext.Provider>

161

</SettingsContext.Provider>

162

</UserContext.Provider>

163

);

164

}

165

166

// Custom hooks for easier context consumption

167

function useUser() {

168

const user = useContext(UserContext);

169

const isAuthenticated = createMemo(() => user !== null);

170

const isAdmin = createMemo(() => user?.role === "admin");

171

172

return { user, isAuthenticated: isAuthenticated(), isAdmin: isAdmin() };

173

}

174

175

function useSettings() {

176

return useContext(SettingsContext);

177

}

178

179

// Component using multiple contexts

180

function UserDashboard() {

181

const { user, isAuthenticated, isAdmin } = useUser();

182

const settings = useSettings();

183

const actions = useContext(ActionsContext);

184

185

return (

186

<div>

187

<Show when={isAuthenticated} fallback={<LoginForm />}>

188

<div>

189

<h1>Welcome, {user!.name}!</h1>

190

<p>Language: {settings.language}</p>

191

<p>Theme: {settings.theme}</p>

192

193

<Show when={isAdmin}>

194

<AdminPanel />

195

</Show>

196

197

<button onClick={actions.logout}>Logout</button>

198

</div>

199

</Show>

200

</div>

201

);

202

}

203

```

204

205

### Reactive Ownership and Scoping

206

207

Manage reactive ownership and create isolated reactive scopes.

208

209

```typescript { .api }

210

/**

211

* Creates a new non-tracked reactive context that doesn't auto-dispose

212

* @param fn - Function to run in the new reactive scope

213

* @param detachedOwner - Optional owner to detach from

214

* @returns Return value of the function

215

*/

216

function createRoot<T>(

217

fn: (dispose: () => void) => T,

218

detachedOwner?: Owner

219

): T;

220

221

/**

222

* Gets the current reactive owner

223

* @returns Current owner or null

224

*/

225

function getOwner(): Owner | null;

226

227

/**

228

* Runs a function with a specific reactive owner

229

* @param owner - Owner to run the function with

230

* @param fn - Function to run

231

* @returns Return value of the function or undefined

232

*/

233

function runWithOwner<T>(

234

owner: Owner | null,

235

fn: () => T

236

): T | undefined;

237

238

interface Owner {

239

owned: Computation<any>[] | null;

240

cleanups: (() => void)[] | null;

241

owner: Owner | null;

242

context: any | null;

243

sourceMap?: SourceMapValue[];

244

name?: string;

245

}

246

```

247

248

**Usage Examples:**

249

250

```typescript

251

import { createRoot, createSignal, createEffect, getOwner, runWithOwner } from "solid-js";

252

253

// Creating isolated reactive scopes

254

function IsolatedComponent() {

255

let dispose: (() => void) | undefined;

256

257

const cleanup = () => {

258

if (dispose) {

259

dispose();

260

dispose = undefined;

261

}

262

};

263

264

const initialize = () => {

265

cleanup(); // Clean up previous scope if it exists

266

267

createRoot((disposeScope) => {

268

dispose = disposeScope;

269

270

const [count, setCount] = createSignal(0);

271

272

createEffect(() => {

273

console.log("Isolated count:", count());

274

});

275

276

// Set up some interval that will be cleaned up

277

const interval = setInterval(() => {

278

setCount(c => c + 1);

279

}, 1000);

280

281

onCleanup(() => {

282

clearInterval(interval);

283

console.log("Isolated scope cleaned up");

284

});

285

286

return { count, setCount };

287

});

288

};

289

290

onCleanup(cleanup);

291

292

return (

293

<div>

294

<button onClick={initialize}>Initialize Isolated Scope</button>

295

<button onClick={cleanup}>Cleanup Scope</button>

296

</div>

297

);

298

}

299

300

// Working with owners for advanced patterns

301

function OwnerExample() {

302

const [items, setItems] = createSignal<{ id: number; owner: Owner | null }[]>([]);

303

304

const addItem = () => {

305

const owner = getOwner();

306

setItems(prev => [...prev, { id: Date.now(), owner }]);

307

};

308

309

const runInOriginalOwner = (item: { id: number; owner: Owner | null }) => {

310

runWithOwner(item.owner, () => {

311

console.log("Running in original owner context");

312

// This runs in the context where the item was created

313

});

314

};

315

316

return (

317

<div>

318

<button onClick={addItem}>Add Item</button>

319

<For each={items()}>

320

{(item) => (

321

<div>

322

Item {item.id}

323

<button onClick={() => runInOriginalOwner(item)}>

324

Run in Original Context

325

</button>

326

</div>

327

)}

328

</For>

329

</div>

330

);

331

}

332

```

333

334

### Transitions and Scheduling

335

336

Manage reactive updates with transitions and custom scheduling.

337

338

```typescript { .api }

339

/**

340

* Wraps updates in a low-priority transition

341

* @param fn - Function containing updates to run in transition

342

* @returns Promise that resolves when transition completes

343

*/

344

function startTransition(fn: () => void): Promise<void>;

345

346

/**

347

* Returns a tuple with pending accessor and startTransition function

348

* @returns Tuple of [isPending accessor, startTransition function]

349

*/

350

function useTransition(): [Accessor<boolean>, (fn: () => void) => Promise<void>];

351

```

352

353

**Usage Examples:**

354

355

```typescript

356

import { useTransition, createSignal, For, createMemo } from "solid-js";

357

358

function TransitionExample() {

359

const [query, setQuery] = createSignal("");

360

const [items, setItems] = createSignal(generateItems(1000));

361

const [isPending, startTransition] = useTransition();

362

363

// Expensive filtering operation

364

const filteredItems = createMemo(() => {

365

const q = query().toLowerCase();

366

return items().filter(item =>

367

item.name.toLowerCase().includes(q) ||

368

item.description.toLowerCase().includes(q)

369

);

370

});

371

372

const handleSearch = (value: string) => {

373

// Update query immediately for responsive input

374

setQuery(value);

375

376

// Wrap expensive filtering in transition

377

startTransition(() => {

378

// This could trigger expensive computations

379

console.log("Filtering items...");

380

});

381

};

382

383

return (

384

<div>

385

<input

386

type="text"

387

placeholder="Search items..."

388

value={query()}

389

onInput={(e) => handleSearch(e.target.value)}

390

/>

391

392

<div>

393

{isPending() && <div class="loading">Filtering...</div>}

394

<p>Found {filteredItems().length} items</p>

395

</div>

396

397

<div class="items">

398

<For each={filteredItems()}>

399

{(item) => (

400

<div class="item">

401

<h3>{item.name}</h3>

402

<p>{item.description}</p>

403

</div>

404

)}

405

</For>

406

</div>

407

</div>

408

);

409

}

410

411

function generateItems(count: number) {

412

return Array.from({ length: count }, (_, i) => ({

413

id: i,

414

name: `Item ${i}`,

415

description: `Description for item ${i}`

416

}));

417

}

418

```

419

420

### Global State Management with Context

421

422

Create global state management using context and reactive primitives.

423

424

**Usage Examples:**

425

426

```typescript

427

import { createContext, useContext, createSignal, createMemo } from "solid-js";

428

429

// Global store interface

430

interface Store {

431

user: User | null;

432

cart: CartItem[];

433

theme: Theme;

434

}

435

436

interface StoreActions {

437

setUser: (user: User | null) => void;

438

addToCart: (item: Product) => void;

439

removeFromCart: (itemId: number) => void;

440

setTheme: (theme: Theme) => void;

441

}

442

443

// Create store context

444

const StoreContext = createContext<Store & StoreActions>();

445

446

// Store provider

447

function StoreProvider(props: { children: JSX.Element }) {

448

const [user, setUser] = createSignal<User | null>(null);

449

const [cart, setCart] = createSignal<CartItem[]>([]);

450

const [theme, setTheme] = createSignal<Theme>({ mode: "light" });

451

452

const addToCart = (product: Product) => {

453

setCart(prev => {

454

const existing = prev.find(item => item.product.id === product.id);

455

if (existing) {

456

return prev.map(item =>

457

item.product.id === product.id

458

? { ...item, quantity: item.quantity + 1 }

459

: item

460

);

461

}

462

return [...prev, { product, quantity: 1 }];

463

});

464

};

465

466

const removeFromCart = (productId: number) => {

467

setCart(prev => prev.filter(item => item.product.id !== productId));

468

};

469

470

const store = createMemo(() => ({

471

user: user(),

472

cart: cart(),

473

theme: theme(),

474

setUser,

475

addToCart,

476

removeFromCart,

477

setTheme

478

}));

479

480

return (

481

<StoreContext.Provider value={store()}>

482

{props.children}

483

</StoreContext.Provider>

484

);

485

}

486

487

// Custom hook to use store

488

function useStore() {

489

const store = useContext(StoreContext);

490

if (!store) {

491

throw new Error("useStore must be used within StoreProvider");

492

}

493

return store;

494

}

495

496

// Components using the global store

497

function ShoppingCart() {

498

const { cart, removeFromCart } = useStore();

499

500

const total = createMemo(() =>

501

cart.reduce((sum, item) => sum + item.product.price * item.quantity, 0)

502

);

503

504

return (

505

<div>

506

<h2>Shopping Cart</h2>

507

<For each={cart} fallback={<p>Cart is empty</p>}>

508

{(item) => (

509

<div class="cart-item">

510

<span>{item.product.name} x {item.quantity}</span>

511

<span>${(item.product.price * item.quantity).toFixed(2)}</span>

512

<button onClick={() => removeFromCart(item.product.id)}>

513

Remove

514

</button>

515

</div>

516

)}

517

</For>

518

<div class="total">Total: ${total().toFixed(2)}</div>

519

</div>

520

);

521

}

522

523

function ProductList() {

524

const { addToCart } = useStore();

525

const [products] = createResource(() => fetchProducts());

526

527

return (

528

<div>

529

<h2>Products</h2>

530

<For each={products()}>

531

{(product) => (

532

<div class="product">

533

<h3>{product.name}</h3>

534

<p>${product.price}</p>

535

<button onClick={() => addToCart(product)}>

536

Add to Cart

537

</button>

538

</div>

539

)}

540

</For>

541

</div>

542

);

543

}

544

```