or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compat.mdcomponents.mdcontext.mdcore.mddevtools.mdhooks.mdindex.mdjsx-runtime.mdtesting.md

components.mddocs/

0

# Component System

1

2

Class-based and functional component patterns with complete lifecycle methods and state management capabilities. Preact supports both React-style class components and modern functional components.

3

4

## Capabilities

5

6

### Component Base Class

7

8

Base class for creating stateful components with lifecycle methods and local state management.

9

10

```typescript { .api }

11

/**

12

* Base class for stateful components

13

*/

14

abstract class Component<P = {}, S = {}> {

15

constructor(props?: P, context?: any);

16

17

// Static properties

18

static displayName?: string;

19

static defaultProps?: any;

20

static contextType?: Context<any>;

21

22

// Static lifecycle methods

23

static getDerivedStateFromProps?(props: Readonly<object>, state: Readonly<object>): object | null;

24

static getDerivedStateFromError?(error: any): object | null;

25

26

// Instance properties

27

state: Readonly<S>;

28

props: RenderableProps<P>;

29

context: any;

30

base?: Element | Text;

31

32

// State management

33

setState<K extends keyof S>(

34

state: ((prevState: Readonly<S>, props: Readonly<P>) => Pick<S, K> | Partial<S> | null) |

35

(Pick<S, K> | Partial<S> | null),

36

callback?: () => void

37

): void;

38

39

forceUpdate(callback?: () => void): void;

40

41

// Lifecycle methods (optional)

42

componentWillMount?(): void;

43

componentDidMount?(): void;

44

componentWillUnmount?(): void;

45

componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;

46

shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;

47

componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;

48

getSnapshotBeforeUpdate?(oldProps: Readonly<P>, oldState: Readonly<S>): any;

49

componentDidUpdate?(previousProps: Readonly<P>, previousState: Readonly<S>, snapshot: any): void;

50

componentDidCatch?(error: any, errorInfo: ErrorInfo): void;

51

getChildContext?(): object;

52

53

// Abstract render method

54

abstract render(props?: RenderableProps<P>, state?: Readonly<S>, context?: any): ComponentChildren;

55

}

56

57

interface ErrorInfo {

58

componentStack?: string;

59

}

60

```

61

62

**Usage Examples:**

63

64

```typescript

65

import { Component, createElement } from "preact";

66

67

// Basic class component

68

class Counter extends Component<{}, { count: number }> {

69

state = { count: 0 };

70

71

increment = () => {

72

this.setState({ count: this.state.count + 1 });

73

};

74

75

render() {

76

return createElement("div", null,

77

createElement("p", null, `Count: ${this.state.count}`),

78

createElement("button", { onClick: this.increment }, "Increment")

79

);

80

}

81

}

82

83

// Component with props and lifecycle

84

interface TimerProps {

85

interval: number;

86

onTick: (count: number) => void;

87

}

88

89

interface TimerState {

90

count: number;

91

}

92

93

class Timer extends Component<TimerProps, TimerState> {

94

private intervalId?: number;

95

96

constructor(props: TimerProps) {

97

super(props);

98

this.state = { count: 0 };

99

}

100

101

componentDidMount() {

102

this.intervalId = window.setInterval(() => {

103

this.setState(prevState => ({ count: prevState.count + 1 }), () => {

104

this.props.onTick(this.state.count);

105

});

106

}, this.props.interval);

107

}

108

109

componentWillUnmount() {

110

if (this.intervalId) {

111

clearInterval(this.intervalId);

112

}

113

}

114

115

shouldComponentUpdate(nextProps: TimerProps, nextState: TimerState) {

116

return nextState.count !== this.state.count || nextProps.interval !== this.props.interval;

117

}

118

119

render() {

120

return createElement("div", null, `Timer: ${this.state.count}`);

121

}

122

}

123

```

124

125

### Functional Components

126

127

Function-based components for stateless presentation and modern hook-based logic.

128

129

```typescript { .api }

130

/**

131

* Functional component interface

132

*/

133

interface FunctionComponent<P = {}> {

134

(props: RenderableProps<P>, context?: any): ComponentChildren;

135

displayName?: string;

136

defaultProps?: Partial<P> | undefined;

137

}

138

139

// Type alias

140

interface FunctionalComponent<P = {}> extends FunctionComponent<P> {}

141

142

// Generic component type

143

type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;

144

145

// Component constructor interface

146

interface ComponentClass<P = {}, S = {}> {

147

new (props: P, context?: any): Component<P, S>;

148

displayName?: string;

149

defaultProps?: Partial<P>;

150

contextType?: Context<any>;

151

getDerivedStateFromProps?(props: Readonly<P>, state: Readonly<S>): Partial<S> | null;

152

getDerivedStateFromError?(error: any): Partial<S> | null;

153

}

154

```

155

156

**Usage Examples:**

157

158

```typescript

159

import { createElement, FunctionComponent } from "preact";

160

161

// Basic functional component

162

function Greeting({ name }: { name: string }) {

163

return createElement("h1", null, `Hello ${name}!`);

164

}

165

166

// Functional component with default props

167

const Button: FunctionComponent<{ text?: string; onClick?: () => void }> = ({

168

text = "Click me",

169

onClick

170

}) => {

171

return createElement("button", { onClick }, text);

172

};

173

Button.defaultProps = { text: "Default Text" };

174

175

// Component with children

176

interface CardProps {

177

title: string;

178

children?: ComponentChildren;

179

}

180

181

const Card: FunctionComponent<CardProps> = ({ title, children }) => {

182

return createElement("div", { className: "card" },

183

createElement("h2", { className: "card-title" }, title),

184

createElement("div", { className: "card-content" }, children)

185

);

186

};

187

188

// Using the card component

189

const app = createElement(Card, { title: "My Card" },

190

createElement("p", null, "This is the content"),

191

createElement("button", null, "Action")

192

);

193

```

194

195

### Component Lifecycle

196

197

Detailed lifecycle methods available in class components for managing component behavior throughout its existence.

198

199

```typescript { .api }

200

/**

201

* Component lifecycle methods

202

*/

203

interface Component<P = {}, S = {}> {

204

// Mounting phase

205

componentWillMount?(): void;

206

componentDidMount?(): void;

207

208

// Updating phase

209

componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;

210

shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;

211

componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;

212

getSnapshotBeforeUpdate?(oldProps: Readonly<P>, oldState: Readonly<S>): any;

213

componentDidUpdate?(previousProps: Readonly<P>, previousState: Readonly<S>, snapshot: any): void;

214

215

// Unmounting phase

216

componentWillUnmount?(): void;

217

218

// Error handling

219

componentDidCatch?(error: any, errorInfo: ErrorInfo): void;

220

221

// Context

222

getChildContext?(): object;

223

}

224

```

225

226

**Usage Examples:**

227

228

```typescript

229

import { Component, createElement } from "preact";

230

231

class LifecycleDemo extends Component<{ userId: number }, { user: any; loading: boolean }> {

232

state = { user: null, loading: true };

233

234

async componentDidMount() {

235

console.log("Component mounted");

236

await this.fetchUser(this.props.userId);

237

}

238

239

componentWillReceiveProps(nextProps: { userId: number }) {

240

if (nextProps.userId !== this.props.userId) {

241

this.setState({ loading: true });

242

this.fetchUser(nextProps.userId);

243

}

244

}

245

246

shouldComponentUpdate(nextProps: { userId: number }, nextState: any) {

247

return nextProps.userId !== this.props.userId ||

248

nextState.user !== this.state.user ||

249

nextState.loading !== this.state.loading;

250

}

251

252

getSnapshotBeforeUpdate() {

253

return { scrollTop: document.documentElement.scrollTop };

254

}

255

256

componentDidUpdate(prevProps: { userId: number }, prevState: any, snapshot: any) {

257

console.log("Component updated", { prevProps, snapshot });

258

}

259

260

componentWillUnmount() {

261

console.log("Component will unmount");

262

// Cleanup subscriptions, timers, etc.

263

}

264

265

componentDidCatch(error: any, errorInfo: any) {

266

console.error("Component caught error:", error, errorInfo);

267

this.setState({ user: null, loading: false });

268

}

269

270

private async fetchUser(userId: number) {

271

try {

272

const response = await fetch(`/api/users/${userId}`);

273

const user = await response.json();

274

this.setState({ user, loading: false });

275

} catch (error) {

276

this.setState({ user: null, loading: false });

277

}

278

}

279

280

render() {

281

if (this.state.loading) {

282

return createElement("div", null, "Loading...");

283

}

284

285

if (!this.state.user) {

286

return createElement("div", null, "User not found");

287

}

288

289

return createElement("div", null,

290

createElement("h1", null, this.state.user.name),

291

createElement("p", null, this.state.user.email)

292

);

293

}

294

}

295

```

296

297

### Component Props and State Types

298

299

Type definitions for component properties, state, and related interfaces.

300

301

```typescript { .api }

302

/**

303

* Props with children and ref support

304

*/

305

type RenderableProps<P, RefType = any> = P &

306

Readonly<Attributes & { children?: ComponentChildren; ref?: Ref<RefType> }>;

307

308

/**

309

* Props for specific component types

310

*/

311

type ComponentProps<C extends ComponentType<any> | keyof JSXInternal.IntrinsicElements> =

312

C extends ComponentType<infer P>

313

? P

314

: C extends keyof JSXInternal.IntrinsicElements

315

? JSXInternal.IntrinsicElements[C]

316

: {};

317

318

/**

319

* Base attributes interface

320

*/

321

interface Attributes {

322

key?: Key | undefined;

323

jsx?: boolean | undefined;

324

}

325

326

/**

327

* Attributes with ref support

328

*/

329

interface ClassAttributes<T> extends Attributes {

330

ref?: Ref<T>;

331

}

332

333

/**

334

* General component type union

335

*/

336

type AnyComponent<P = {}, S = {}> = FunctionComponent<P> | ComponentConstructor<P, S>;

337

338

interface ComponentConstructor<P = {}, S = {}> extends ComponentClass<P, S> {}

339

```

340

341

**Usage Examples:**

342

343

```typescript

344

import { ComponentProps, RenderableProps, createElement } from "preact";

345

346

// Using ComponentProps to extract props from existing components

347

type ButtonProps = ComponentProps<"button">;

348

type CustomButtonProps = ComponentProps<typeof CustomButton>;

349

350

// Component with properly typed props

351

interface UserCardProps {

352

user: {

353

id: number;

354

name: string;

355

email: string;

356

};

357

onEdit?: (id: number) => void;

358

}

359

360

function UserCard({ user, onEdit, children }: RenderableProps<UserCardProps>) {

361

return createElement("div", { className: "user-card" },

362

createElement("h3", null, user.name),

363

createElement("p", null, user.email),

364

onEdit && createElement("button",

365

{ onClick: () => onEdit(user.id) },

366

"Edit"

367

),

368

children

369

);

370

}

371

372

// Generic component with constraints

373

function List<T>({

374

items,

375

renderItem,

376

keyExtractor

377

}: RenderableProps<{

378

items: T[];

379

renderItem: (item: T, index: number) => ComponentChildren;

380

keyExtractor: (item: T, index: number) => Key;

381

}>) {

382

return createElement("ul", null,

383

items.map((item, index) =>

384

createElement("li",

385

{ key: keyExtractor(item, index) },

386

renderItem(item, index)

387

)

388

)

389

);

390

}

391

```