# Components
In rvx, components are functions that return any type of [supported content](elements.md#content).

=== "JSX"
	```jsx
	function Message() {
		return <h1>Hello World!</h1>;
	}

	<Message />
	```

=== "No Build"
	```jsx
	function Message() {
		return e("h1").append("Hello World!");
	}

	Message()
	```

## Properties
Properties are passed via the first argument as is:

=== "JSX"
	```jsx
	function Message(props: { text: string; }) {
		return <h1>{props.text}</h1>;
	}

	<Message text="Hello World!" />
	```

=== "No Build"
	```jsx
	/**
	 * @param {object} props
	 * @param {string} props.text
	 */
	function Message(props) {
		return e("h1").append(props.text);
	}

	Message({ text: "Hello World!" })
	```

In JSX, children are passed as the `children` property.

=== "JSX"
	```jsx
	function Message(props: { children?: unknown; }) {
		return <h1>{props.children}</h1>;
	}

	<Message>Hello World!</Message>
	```

=== "No Build"
	When not using JSX, the `children` property is still recommended for consistency, but not a requirement.

	```jsx
	/**
	 * @param {object} props
	 * @param {unknown} props.children
	 */
	function Message(props) {
		return e("h1").append(props.children);
	}

	Message({ children: "Hello World!" })
	```

### Expressions
By default, all component properties are static. To accept reactive inputs, use the [`Expression`](signals.md#expressions) type.

=== "JSX"
	```jsx
	import { $, Expression } from "rvx";

	function Counter(props: { value: Expression<number>; }) {
		return <>Current count: {props.value}</>;
	}

	const count = $(42);

	// Static values:
	<Counter value={count.value} />

	// Reactive values:
	<Counter value={count} />
	<Counter value={() => count.value} />
	```

=== "No Build"
	```jsx
	import { $ } from "./rvx.js";

	/**
	 * @param {object} props
	 * @param {import("./rvx.js").Expression<number>} props.value
	 */
	function Counter(props) {
		return ["Current count: ", props.value];
	}

	const count = $(42);

	// Static values:
	Counter({ count: 42 })
	Counter({ count: count.value })

	// Reactive values:
	Counter({ count: count })
	Counter({ count: () => count.value })
	```

In cases where static values never make sense, you can use `Reactive` instead of the `Expression` type to disallow static values:

=== "JSX"
	```jsx
	import { Reactive } from "rvx";

	function Counter(props: { value: Reactive<number>; }) {
		return <>Current count: {props.value}</>;
	}
	```

=== "No Build"
	```jsx
	/**
	 * @param {object} props
	 * @param {import("./rvx.js").Reactive<number>} props.value
	 */
	function Counter(props) {
		return ["Current count: ", props.value];
	}
	```

### Signals
To support data flow in both directions, you can use [signals](signals.md) as properties.

=== "JSX"
	```jsx
	import { $, Signal } from "rvx";

	function Counter(props: { value: Signal<number>; }) {
		return <button on:click={() => { props.value.value++ }}>
			Count: {props.value}
		</button>;
	}

	const count = $(0);
	<Counter value={count} />
	```

=== "No Build"
	```jsx
	import { $, e } from "./rvx.js";

	/**
	 * @param {object} props
	 * @param {import("./rvx.js").Signal<number>} props.value
	 */
	function Counter(props) {
		return e("button").on("click", () => { props.value.value++ }).append(
			"Count: ", props.value,
		);
	}

	const count = $(0);
	Counter({ value: count })
	```

Using signals for two way data flow also allows converting values in both directions in a nicely composable way.

The example below shows a basic text input and a `trim` function for trimming user input:

=== "JSX"
	```jsx
	import { $, Signal, watchUpdates } from "rvx";

	function TextInput(props: { value: Signal<string>; }) {
		return <input
			type="text"
			prop:value={props.value}
			on:input={event => {
				props.value.value = (event.target as HTMLInputElement).value;
			}}
		/>;
	}

	function trim(source: Signal<string>) {
		// The second parameter is metadata to let other APIs
		// know that "input" has been derived from "source":
		const input = $(source.value, source);

		// Update the source signal if the input changes:
		watchUpdates(input, value => {
			source.value = value.trim();
		});

		// Update the input signal if the source changes:
		watchUpdates(source, value => {
			if (value !== input.value.trim()) {
				input.value = value;
			}
		});

		return input;
	}

	const text = $("");

	// This input uses the "text" signal as is:
	<TextInput value={text} />

	// This input uses the "trim" function to store the
	// trimmed version of the input in the "text" signal:
	<TextInput value={trim(text)} />

	// The signal's pipe function does the same but is more
	// readable when using multiple derivations:
	<TextInput value={text.pipe(trim).pipe(...)} />
	```

=== "No Build"
	```jsx
	import { $, watchUpdates, e } from "./rvx.js";

	/**
	 * @param {object} props
	 * @param {import("./rvx.js").Signal<string>} props.value
	 */
	function TextInput(props) {
		return e("input")
			.set("type", "text")
			.prop("value", props.value)
			.on("input", event => props.value.value = event.target.value);
	}

	/**
	 * @param {import("./rvx.js").Signal<string>} source
	 */
	function trim(source) {
		// The second parameter is metadata to let other APIs
		// know that "input" has been derived from "source":
		const input = $(source.value, source);

		// Update the source signal if the input changes:
		watchUpdates(input, value => {
			source.value = value.trim();
		});

		// Update the input signal if the source changes:
		watchUpdates(source, value => {
			if (value !== input.value.trim()) {
				input.value = value;
			}
		});

		return input;
	}

	const text = $("");

	// This input uses the "text" signal as is:
	TextInput({ value: text })

	// This input uses the "trim" function to store the
	// trimmed version of the input in the "text" signal:
	TextInput({ value: trim(text) })

	// The signal's pipe function does the same but is more
	// readable when using multiple derivations:
	TextInput({ value: text.pipe(trim).pipe(...) })
	```

### Forwarding special attributes
Sometimes it can be useful to forward properties to the root element of a component for allowing the user of the component to set the `class`, `style` or any other attributes.

=== "JSX"
	```jsx
	import { ClassValue, StyleValue } from "rvx";

	function Button(props: {
		class?: ClassValue;
		style?: StyleValue;
		id?: Expression<string | undefined>;
		...
	}) {
		return <button
			class={props.class}
			style={props.style}
			id={props.id}
		>...</button>;
	}
	```

=== "No Build"
	```jsx
	import { e } from "./rvx.js";

	/**
	 * @param {object} props
	 * @param {import("./rvx.js").ClassValue} props.class
	 * @param {import("./rvx.js").StyleValue} props.style
	 * @param {import("./rvx.js").Expression<string | undefined>} props.id
	 */
	function Button(props) {
		return e("button")
			.class(props.class)
			.style(props.style)
			.set("id", props.id)
			.append(...);
	}
	```

In case of the `class` and `style` attributes, you can use an array as value to mix properties with values from within your component:

=== "JSX"
	```jsx
	import { ClassValue, StyleValue } from "rvx";

	function Button(props: {
		class?: ClassValue;
		style?: StyleValue;
		...
	}) {
		return <button
			class={[props.class, "example"]}
			style={[props.style, { color: "red" }]}
			...
		>...</button>;
	}
	```

=== "No Build"
	```jsx
	import { e } from "./rvx.js";

	/**
	 * @param {object} props
	 * @param {import("./rvx.js").ClassValue} props.class
	 * @param {import("./rvx.js").StyleValue} props.style
	 */
	function Button(props) {
		return e("button")
			.class([props.class, "example"])
			.style([props.style, { color: "red" }])
			.append(...);
	}
	```

## Lifecycle Hooks
[Lifecycle hooks](./lifecycle.md) are supported in components:

=== "JSX"
	```jsx
	import { teardown } from "rvx";

	function Timer() {
		const elapsed = $(0);
		const timer = setInterval(() => { elapsed.value++ }, 1000);
		teardown(() => clearInterval(timer));
		return elapsed;
	}
	```

=== "No Build"
	```jsx
	import { teardown } from "./rvx.js";

	function Timer() {
		const elapsed = $(0);
		const timer = setInterval(() => { elapsed.value++ }, 1000);
		teardown(() => clearInterval(timer));
		return elapsed;
	}
	```
