# React, Vue 3, Svelte, and SolidJS Cheatsheets
## Template Bindings
### React
```jsx
export default function App() {
const name = "John Wick";
const count = 1;
const toggled = true;
const html = "<strong>Highlighted</strong>";
return (
<>
<p>Hello {name}</p>
<p>{`Hello ${name}`}</p>
<p>Count: {count}, Doubled {count * 2}</p>
<p>Toggled: {toggled ? "👀" : "🙈"}</p>
<p dangerouslySetInnerHTML={{ __html: html }}></p>
</>
);
}
```
### Vue 3
```htmlmixed
<template>
<p>Hello {{ name }}</p>
<p>{{ `Hello ${name}` }}</p>
<p>Count: {{ count }}, Doubled {{ count * 2 }}</p>
<p>Toggled: {{ toggled ? "👀" : "🙈" }}</p>
<p v-html="html"></p>
</template>
<script>
export default {
setup() {
const name = "John Wick";
const count = 1;
const toggled = true;
const html = "<strong>Highlighted</strong>";
return {
name,
count,
toggled,
html
}
}
}
</script>
```
or, using the new `setup` script type in Vue 3.2+,
```htmlmixed
<template>
<p>Hello {{ name }}</p>
<p>{{ `Hello ${name}` }}</p>
<p>Count: {{ count }}, Doubled {{ count * 2 }}</p>
<p>Toggled: {{ toggled ? "👀" : "🙈" }}</p>
<p v-html="html"></p>
</template>
<script setup>
const name = "John Wick";
const count = 1;
const toggled = true;
const html = "<strong>Highlighted</strong>";
</script>
```
**_The latter approach will be used for the rest of the examples moving forward._**
### Svelte
```htmlmixed
<script>
let name = "John Wick";
let count = 1;
let toggled = true;
let html = "<strong>Highlighted</strong>";
</script>
<p>Hello {name}</p>
<p>{`Hello ${name}`}</p>
<p>Count: {count}, Doubled {count * 2}</p>
<p>Toggled: {toggled ? "👀" : "🙈"}</p>
<p>{@html "<em>Emphasized</em>"}</p>
<p>{@html html}</p>
```
### SolidJS
```jsx
export default function App() {
const name = "John Wick";
const count = 1;
const toggled = true;
const html = "<strong>Highlighted</strong>";
return (
<>
<p>Hello {name}</p>
<p>{`Hello ${name}`}</p>
<p>Count: {count}, Doubled {count * 2}</p>
<p>Toggled: {toggled ? "👀" : "🙈"}</p>
<p innerHTML="<em>Emphasized</em>"></p>
<p innerHTML={html}></p>
</>
);
}
```
## Reactivity
### React
```jsx=
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const [numbers, setNumbers] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const [profile, setProfile] = useState({ name: "John Wick", active: true });
return (
<>
{/* Update primitive type */}
<button onClick={() => setCount((count) => count + 1)}>Count: {count}</button>
{/* Update array */}
<button
onClick={() =>
setNumbers((numbers) => [...numbers, numbers.length + 1])
}
>
{JSON.stringify(numbers)}
</button>
{/* Update object */}
<button
onClick={() =>
setProfile((profile) => ({ ...profile, active: !profile.active }))
}
>
{JSON.stringify(profile)}
</button>
</>
);
}
```
### Vue 3
```jsx=
<template>
<!-- Update primitive type -->
<button @click="count++">Count: {{ count }}</button>
<!-- Update array -->
<button @click="numbers.push(numbers.length + 1)">
{{ JSON.stringify(numbers) }}
</button>
<!-- Update object -->
<button @click="profile.active = !profile.active">
{{ JSON.stringify(profile) }}
</button>
</template>
<script setup>
import { reactive, ref } from "vue";
const count = ref(0);
const numbers = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const profile = reactive({ name: "John Wick", active: true });
</script>
```
### Svelte
```jsx=
<script>
let count = 0;
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let profile = { name: "John Wick", active: true };
</script>
<!-- Update primitive type -->
<button on:click={() => count++}>Count: {count}</button>
<!-- Update array -->
<button on:click={() => numbers = [...numbers, numbers.length + 1]}>
{JSON.stringify(numbers)}
</button>
<!-- Update object -->
<button on:click={() => profile = { ...profile, active: !profile.active }}>
{JSON.stringify(profile)}
</button>}
```
### SolidJS
```jsx=
import { createSignal } from "solid-js";
export default function App() {
const [count, setCount] = createSignal(0);
const [numbers, setNumbers] = createSignal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const [profile, setProfile] = createSignal({ name: "John Wick", active: true });
return (
<>
{/* Update primitive type */}
<button onClick={() => setCount((count) => count + 1)}>Count: {count()}</button>
{/* Update array */}
<button
onClick={() =>
setNumbers((numbers) => [...numbers, numbers.length + 1])
}
>
{JSON.stringify(numbers())}
</button>
{/* Update object */}
<button
onClick={() =>
setProfile((profile) => ({ ...profile, active: !profile.active }))
}
>
{JSON.stringify(profile())}
</button>
</>
);
}
```
## Computed/Derived Values
### React
```jsx=
import { useMemo, useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const doubled = count * 2;
// ...or memoize if doing expensive computations:
// const doubled = useMemo(() => count * 2, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count} Doubled: {doubled}
</button>
);
}
```
### Vue 3
```jsx=
<template>
<button @click="count++">
Count: {{ count }} Doubled: {{ doubled }}
</button>
</template>
<script setup>
import { computed, ref } from "vue";
const count = ref(0);
const doubled = computed(() => count.value * 2);
</script>
```
### Svelte
```jsx=
<script>
let count = 0;
$: doubled = count * 2;
</script>
<button on:click={() => count++}>
Count: {count} Doubled: {doubled}
</button>
```
### SolidJS
```jsx=
import { createMemo, createSignal } from "solid-js";
export default function App() {
const [count, setCount] = createSignal(0);
const doubled = count() * 2;
// ...or cache results if doing expensive computations:
// const doubled = createMemo(() => count() * 2);
return (
<button onClick={() => setCount((count) => count + 1)}>
Count: {count} Doubled: {doubled}
</button>
);
}
```
## Loops
### React
```jsx=
import { useState } from "react";
export default function App() {
const [todos, setTodos] = useState([
{ id: 1, title: "Clean room" },
{ id: 2, title: "Wash dishes" },
{ id: 3, title: "Buy milk" }
])
return (
<ul>
{todos.map((todo, index) => (
<li key={todo.id}>
{index}: {todo.title}
</li>
))}
</ul>
);
}
```
### Vue 3
```jsx=
<template>
<ul>
<li v-for="todo, index in todos" :key="todo.id">
{{ index }}: {{ todo.title }}
</li>
</ul>
</template>
<script setup>
import { ref } from "vue";
const todos = ref([
{ id: 1, title: "Clean room" },
{ id: 2, title: "Wash dishes" },
{ id: 3, title: "Buy milk" }
]);
</script>
```
### Svelte
```jsx=
<script>
let todos = [
{ id: 1, title: "Clean room" },
{ id: 2, title: "Wash dishes" },
{ id: 3, title: "Buy milk" }
];
</script>
<ul>
{#each todos as todo, index (todo.id)}
<li>
{index}: {todo.title}
</li>
{:else}
<li>Nothing to do.</li>
{/each}
</ul>
```
### SolidJS
```jsx=
import { createSignal, Index, For } from "solid-js";
export default function App() {
const [todos, setTodos] = createSignal([
{ id: 1, title: "Clean room" },
{ id: 2, title: "Wash dishes" },
{ id: 3, title: "Buy milk" }
])
return (
<ul>
{/* This works similar to React (non-keyed) */}
{todos().map((todo, index) => (
<li>
{index}: {todo.title}
</li>
))}
{/* ..but this is the recommended method (keyed by reference) */}
<For each={todos()}>
{(todo, index) =>
<li>
{index()}: {todo.title}
</li>
}
</For>
{/* ..or this (keyed by index) */}
<Index each={todos()}>
{(todo, index) =>
<li>
{index}: {todo().title}
</li>
}
</Index>
</ul>
);
}
```
## Conditional Rendering
### React
```jsx=
import { useState } from "react";
export default function App() {
const [toggled, setToggled] = useState(false);
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setToggled((toggled) => !toggled)}>Toggle</button>
{toggled && <p>Visible</p>}
{toggled ? <p>Toggle: On</p> : <p>Toggle: Off</p>}
<button onClick={() => setCount((count) => count + 1)}>
Count: {count}
</button>
{count % 2 === 0 ? (
<p>Divisible by 2</p>
) : count % 3 === 0 ? (
<p>Divisible by 3</p>
) : (
<p>Not divisible by either 2 or 3</p>
)}
</>
);
}
```
### Vue 3
```jsx=
<template>
<button @click="toggled = !toggled">
Toggle
</button>
<p v-if="toggled">Visible</p>
<p v-if="toggled">Toggle: On</p>
<p v-else>Toggle: Off</p>
<button @click="count++">Count: {{ count }}</button>
<p v-if="count % 2 === 0">Divisible by 2</p>
<p v-else-if="count % 3 === 0">Divisible by 3</p>
<p v-else>Not divisible by either 2 or 3</p>
</template>
<script setup>
import { ref } from "vue";
const toggled = ref(false);
const count = ref(0);
</script>
```
### Svelte
```jsx=
<script>
let toggled = false;
let count = 0;
</script>
<button on:click={() => toggled = !toggled}>
Toggle
</button>
{#if toggled}
<p>Visible</p>
{/if}
{#if toggled}
<p>Toggle: On</p>
{:else}
<p>Toggle: Off</p>
{/if}
<button on:click={() => count++}>
Count: {count}
</button>
{#if count % 2 === 0}
<p>Divisible by 2</p>
{:else if count % 3 === 0}
<p>Divisible by 3</p>
{:else}
<p>Not divisible by either 2 or 3</p>
{/if}
```
### SolidJS
```jsx=
import { createSignal, Show } from "solid-js";
import { createSignal, Match, Show, Switch } from "solid-js";
export default function App() {
const [toggled, setToggled] = createSignal(false);
const [count, setCount] = createSignal(0);
return (
<>
<button onClick={() => setToggled((toggled) => !toggled)}>
Toggle
</button>
{/* This works similar to React */}
{toggled && <p>Visible</p>}f
{toggled() ? <p>Toggle: On</p> : <p>Toggle: Off</p>}
{/* ..but this is the recommended method */}
<Show when={toggled()}><p>Visible</p></Show>
<Show when={toggled()} fallback={<p>Toggle: Off</p>}><p>Toggle: On</p></Show>
<button onClick={() => setCount(count => count + 1)}>
Count: {count()}
</button>
{/* For multiple mutually exclusive conditions */}
<Switch fallback={<p>Not divisible by either 2 or 3</p>}>
<Match when={count() % 2 === 0}>
<p>Divisible by 2</p>
</Match>
<Match when={count() % 3 === 0}>
<p>Divisible by 3</p>
</Match>
</Switch>
</>
);
}
```
## Component Props
The following sets up a `BookCard` component that have optional properties `title` and `author`, and a `ProfileCard` component with an object type property `profile`.
### React
```jsx=
const BookCard = ({ title = "Unknown", author = "Unknown" }) => {
return (
<div>
<p>Title: {title}</p>
<p>Author: {author}</p>
</div>
);
};
const ProfileCard = ({ profile: { name, location }}) => {
return (
<div>
<p>Name: {name}</p>
<p>Location: {location}</p>
</div>
);
}
export default function App() {
const book = {
title: "John Wick #2",
author: "Greg Pak"
};
const profile = {
name: "John Wick",
location: "USA"
};
return (
<>
{/* Use default values for props */}
<BookCard />
{/* Use static values for props */}
<BookCard title="John Wick #1" author="Greg Pak" />
{/* Use dynamic values for props */}
<BookCard title={book.title} author={book.author} />
<ProfileCard profile={profile} />
</>
);
}
```
### Vue 3
```jsx=
<!-- BookCard.vue -->
<template>
<p>Title: {{ title }}</p>
<p>Author: {{ author }}</p>
</template>
<script setup>
defineProps({
title: {
type: String,
default: "Unknown"
},
author: {
type: String,
default: "Unknown"
}
});
</script>
```
```jsx=
<!-- ProfileCard.vue -->
<template>
<p>Name: {{ profile.name }}</p>
<p>Location: {{ profile.location }}</p>
</template>
<script setup>
defineProps({
profile: {
type: Object,
required: true
}
});
</script>
```
```jsx=
<!-- App.vue -->
<template>
<!-- Use default values for props -->
<BookCard />
<!-- Use static values for props -->
<BookCard title="John Wick #1" author="Greg Pak" />
<!-- Use dynamic values for props -->
<BookCard :title="book.title" :author="book.author" />
<ProfileCard :profile="profile" />
</template>
<script setup>
import BookCard from "./BookCard.vue";
import ProfileCard from "./ProfileCard.vue";
const book = {
title: "John Wick #2",
author: "Greg Pak"
};
const profile = {
name: "John Wick",
location: "USA"
};
</script>
```
### Svelte
```jsx=
<!-- BookCard.svelte -->
<script>
export let title = "Unknown";
export let author = "Unknown";
</script>
<div>
<p>Title: {title}</p>
<p>Author: {author}</p>
</div>
```
```jsx=
<!-- ProfileCard.svelte -->
<script>
export let profile;
</script>
<div>
<p>Name: {profile.name}</p>
<p>Location: {profile.location}</p>
</div>
```
```jsx=
<!-- App.svelte -->
<script>
import BookCard from "./BookCard.svelte";
import ProfileCard from "./ProfileCard.svelte";
const book = {
title: "John Wick #2",
author: "Greg Pak"
};
const profile = {
name: "John Wick",
location: "USA"
};
</script>
<!-- Use default values for props -->
<BookCard />
<!-- Use static values for props -->
<BookCard title="John Wick #1" author="Greg Pak" />
<!-- Use dynamic values for props -->
<BookCard title={book.title} author={book.author} />
<!-- Shorthand: Destructure values as props -->
<BookCard {...book} />
<ProfileCard profile={profile} />
<!-- Shorthand: Destructure values as props -->
<ProfileCard {profile} />
```
### SolidJS
```jsx=
import { mergeProps } from "solid-js";
const BookCard = (props) => {
props = mergeProps({ title: "Unknown", author: "Unknown"}, props);
return (
<div>
<p>Title: {props.title}</p>
<p>Author: {props.author}</p>
</div>
)
}
const ProfileCard = (props) => {
return (
<div>
<p>Name: {props.profile.name}</p>
<p>Location: {props.profile.location}</p>
</div>
);
}
export default function App() {
const book = {
title: "John Wick #2",
author: "Greg Pak"
};
const profile = {
name: "John Wick",
location: "USA"
};
return (
<>
{/* Use default values for props */}
<BookCard />
{/* Use static values for props */}
<BookCard title="John Wick #1" author="Greg Pak" />
{/* Use dynamic values for props */}
<BookCard title={book.title} author={book.author} />
<ProfileCard profile={profile} />
</>
);
}
```
## Component Slots
The following sets up a `Layout` component that has a minimum slot requirement for the header and body contents. The sidebar and footer contents are both optional with a default content for the footer. The sidebar is only rendered if provided.
### React
```jsx=
const Layout = ({
header,
children,
sidebar,
footer = <p>Powered by React</p>
}) => {
return (
<div>
<div>{header}</div>
{sidebar && <div>{sidebar}</div>}
<div>{children}</div>
<div>{footer}</div>
</div>
);
};
export default function App() {
return (
<Layout header={<h1>Title</h1>}>
Lorem ipsum dolor sit amet...
</Layout>
);
}
```
### Vue
```jsx=
<-- Layout.vue -->
<template>
<div>
<div>
<slot name="header" />
</div>
<div v-if="$slots.sidebar">
<slot name="sidebar" />
</div>
<div>
<slot />
</div>
<div>
<slot name="footer">
<p>Powered by Vue</p>
</slot>
</div>
</div>
</template>
```
```jsx=
<template>
<Layout>
<template #header>
<h1>Vue App</h1>
</template>
<template #default>
Lorem ipsum dolor sit amet...
</template>
</Layout>
</template>
<script setup>
import Layout from "./Layout.vue";
</script>
```
### Svelte
```jsx=
<!-- Layout.svelte -->
<div>
<div>
<slot name="header" />
</div>
{#if $$slots.sidebar}
<div>
<slot name="sidebar" />
</div>
{/if}
<div>
<slot />
</div>
<div>
<slot name="footer">
<p>Powered by Svelte</p>
</slot>
</div>
</div>
```
```jsx=
<!-- App.svelte -->
<script>
import Layout from "./Layout.svelte";
</script>
<Layout>
<h1 slot="header">Svelte App</h1>
<p>Lorem ipsum dolor sit amet...</p>
</Layout>
```
### SolidJS
```jsx=
import { Show } from "solid-js";
const Layout = (props) => {
return (
<div>
<div>{props.header}</div>
<Show when={props.sidebar}>{props.sidebar}</Show>
<div>{props.children}</div>
<Show when={props.footer} fallback={<p>Powered by SolidJS</p>}>{props.footer}</Show>
</div>
);
};
export default function App() {
return (
<Layout header={<h1>Title</h1>}>
Lorem ipsum dolor sit amet...
</Layout>
);
}
```
## Component Lifecycle
### React
> 🗒 NOTE:
>
> `useEffect`, in a strict sense, is not really a lifecycle hook but more of a hook for synchronizing with external systems (subscriptions, networks and DOM), and should not be used for managing data.
```jsx=
import { useEffect, useState } from "react";
const Message = () => {
useEffect(() => {
console.log("Mounted");
return () => {
console.log("Unmounted");
};
}, []);
return <p>Hello</p>;
};
export default function App() {
const [toggled, setToggled] = useState(false);
return (
<>
<button onClick={() => setToggled((toggled) => !toggled)}>Toggle</button>
{toggled && <Message />}
</>
);
}
```
### Vue 3
```jsx=
<!-- Message.vue -->
<template>
<p>Hello</p>
</template>
<script setup>
import { onMounted, onUnmounted } from "vue";
onMounted(() => {
console.log("Mounted");
});
onUnmounted(() => {
console.log("Unmounted");
});
</script>
```
```jsx=
<!-- App.vue -->
<template>
<button @click="toggled = !toggled">Toggle</button>
<Message v-if="toggled" />
</template>
<script setup>
import { ref } from "vue";
import Message from "./Message.vue";
const toggled = ref(false);
</script>
```
### Svelte
```jsx=
<!-- Message.svelte -->
<script>
import { onMount, onDestroy } from "svelte";
onMount(() => {
console.log("Mounted")
});
onDestroy(() => {
console.log("Unmounted")
});
// Alternatively, onMount() can also return a function that
// gets called when the component unmounts.
onMount(() => {
console.log("Mounted 2");
return () => {
console.log("Unmounted 2");
}
});
</script>
<p>Hello</p>
```
```jsx=
<!-- App.svelte -->
<script>
import Message from "./Message.svelte";
let toggled = false;
</script>
<button on:click={() => toggled = !toggled}>Toggle</button>
{#if toggled}
<Message />
{/if}
```
### SolidJS
```jsx=
import { createSignal, onMount, onCleanup, Show } from "solid-js";
const Message = () => {
onMount(() => console.log("Mounted"));
onCleanup(() => console.log("Unmounted"));
return <p>Hello</p>;
};
export default function App() {
const [toggled, setToggled] = createSignal(false);
return (
<>
<button onClick={() => setToggled((toggled) => !toggled)}>Toggle</button>
<Show when={toggled()}><Message /></Show>
</>
);
}
```
## DOM Events
### React
```jsx=
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const increment = (e) => setCount((count) => count + 1);
return (
<>
{/* Handle event inline */}
<button onClick={() => setCount((count) => count + 1)}>Count: {count}</button>
{/* Handle event in function */}
<button onClick={increment}>Count: {count}</button>
</>
);
}
```
### Vue 3
```jsx=
<template>
<!-- Handle event inline -->
<button @click="count++">Count: {{ count }}</button>
<!-- Handle event in function -->
<button @click="increment">Count: {{ count }}</button>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
const increment = (e) => count.value++;
</script>
```
### Svelte
```jsx=
<script>
let count = 0;
const increment = (e) => count++;
</script>
<!-- Handle event inline -->
<button on:click={() => count++}>Count: {count}</button>
<!-- Handle event in function -->
<button on:click={increment}>Count: {count}</button>
```
### SolidJS
```jsx=
import { createSignal } from "solid-js";
export default function App() {
const [count, setCount] = createSignal(0);
const increment = (e) => setCount((count) => count + 1);
return (
<>
{/* Handle event inline */}
<button onClick={() => setCount((count) => count + 1)}>Count: {count()}</button>
{/* Handle event in function */}
<button onClick={increment}>Count: {count}</button>
</>
);
}
```
## Custom Events
### React
```jsx=
import { useCallback, useEffect, useState } from "react";
const Counter = ({ onIncremented }) => {
const [count, setCount] = useState(0);
useEffect(() => {
// Not really a custom event, more of a callback.
onIncremented && onIncremented({ count });
}, [count, onIncremented]);
const increment = () => {
setCount((count) => count + 1);
};
return <button onClick={increment}>Count: {count}</button>;
};
export default function App() {
const [count, setCount] = useState(0);
const handleIncremented = useCallback((e) => {
setCount(e.count);
}, []);
return (
<>
<p>Count: {count}</p>
<Counter onIncremented={handleIncremented} />
</>
);
}
```
### Vue 3
```jsx=
<!-- Counter.vue -->
<template>
<button @click="increment">Count: {{ count }}</button>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
const emit = defineEmits(["incremented"]);
const increment = (e) => {
count.value++;
emit("incremented", { count: count.value });
}
</script>
```
```jsx=
<!-- App.vue -->
<template>
<p>Count: {{ count }}</p>
<Counter @incremented="handleIncremented" />
</template>
<script setup>
import { ref } from "vue";
import Counter from "./Counter.vue";
const count = ref(0);
const handleIncremented = (e) => count.value = e.count;
</script>
```
### Svelte
```jsx=
<!-- Counter.svelte -->
<script>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
let count = 0;
const increment = (e) => {
count++;
dispatch("incremented", { count });
}
</script>
<button on:click={increment}>Count: {count}</button>
```
```jsx=
<!-- App.svelte -->
<script>
import Counter from "./Counter.svelte";
let count = 0;
</script>
<p>Count: {count}</p>
<Counter on:incremented={(e) => count = e.detail.count} />
```
### SolidJS
```jsx=
import { createSignal } from "solid-js";
const Counter = (props) => {
const [count, setCount] = createSignal(0);
const increment = () => {
setCount((count) => count + 1);
// Not really a custom event, more of a callback.
props.onIncremented && props.onIncremented({ count });
};
return <button onClick={increment}>Count: {count}</button>;
};
export default function App() {
const [count, setCount] = createSignal(0);
const handleIncremented = (e) => {
setCount(e.count);
};
return (
<>
<p>Count: {count()}</p>
<Counter onIncremented={handleIncremented} />
</>
);
}
```
## Form Input Bindings
### React
```jsx=
import { useState } from "react";
export default function App() {
const [name, setName] = useState("John Wick");
const [isActive, setIsActive] = useState(true);
const [gender, setGender] = useState("M");
const [rating, setRating] = useState(5);
const [notes, setNotes] = useState("Lorem ipsum dolor sit amet");
return (
<form>
<div>
<label>
Name
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
</div>
<div>
<label>
Active
<input
type="checkbox"
checked={isActive}
onChange={(e) => setIsActive(e.target.checked)}
/>
</label>
</div>
<div>
<label>
Gender
<select
value={gender}
onChange={(e) => setGender(e.target.value)}
>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
</label>
</div>
<div>
Level
<label>
<input
type="radio"
name="rating"
value={1}
checked={rating === 1}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
1
</label>
<label>
<input
type="radio"
name="rating"
value={2}
checked={rating === 2}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
2
</label>
<label>
<input
type="radio"
name="rating"
value={3}
checked={rating === 3}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
3
</label>
<label>
<input
type="radio"
name="rating"
value={4}
checked={rating === 4}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
4
</label>
<label>
<input
type="radio"
name="rating"
value={5}
checked={rating === 5}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
5
</label>
</div>
<div>
<label>
Notes
<textarea
value={notes}
onInput={(e) => setNotes(e.target.value)}
/>
</label>
</div>
</form>
);
}
```
### Vue 3
```jsx=
<template>
<form>
<div>
<label>
Name
<input type="text" v-model="name" />
</label>
</div>
<div>
<label>
Active
<input type="checkbox" v-model="isActive" />
</label>
</div>
<div>
<label>
Gender
<select v-model="gender">
<option value="M">Male</option>
<option value="F">Female</option>
</select>
</label>
</div>
<div>
Level
<label>
<input type="radio" name="rating" v-model="rating" :value="1" /> 1
</label>
<label>
<input type="radio" name="rating" v-model="rating" :value="2" /> 2
</label>
<label>
<input type="radio" name="rating" v-model="rating" :value="3" /> 3
</label>
<label>
<input type="radio" name="rating" v-model="rating" :value="4" /> 4
</label>
<label>
<input type="radio" name="rating" v-model="rating" :value="5" /> 5
</label>
</div>
<div>
<label>
Notes
<textarea v-model="notes" />
</label>
</div>
</form>
</template>
<script setup>
import { ref } from "vue";
const name = ref("John Wick");
const isActive = ref(true);
const gender = ref("M");
const rating = ref(5);
const notes = ref("Lorem ipsum dolor sit amet");
</script>
```
### Svelte
```jsx=
<script>
let name = "John Wick";
let isActive = true;
let gender = "M";
let rating = 5;
let notes = "Lorem ipsum dolor sit amet";
</script>
<form>
<div>
<label>
Name
<input type="text" bind:value={name} />
</label>
</div>
<div>
<label>
Active
<input type="checkbox" bind:checked={isActive} />
</label>
</div>
<div>
<label>
Gender
<select bind:value={gender}>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
</label>
</div>
<div>
Level
<label>
<input type="radio" name="rating" bind:group={rating} value={1} /> 1
</label>
<label>
<input type="radio" name="rating" bind:group={rating} value={2} /> 2
</label>
<label>
<input type="radio" name="rating" bind:group={rating} value={3} /> 3
</label>
<label>
<input type="radio" name="rating" bind:group={rating} value={4} /> 4
</label>
<label>
<input type="radio" name="rating" bind:group={rating} value={5} /> 5
</label>
</div>
<div>
<label>
Notes
<textarea bind:value={notes} />
</label>
</div>
</form>
```
### SolidJS
```jsx=
import { createSignal } from "solid-js";
export default function App() {
const [name, setName] = createSignal("John Wick");
const [isActive, setIsActive] = createSignal(true);
const [gender, setGender] = createSignal("M");
const [rating, setRating] = createSignal(5);
const [notes, setNotes] = createSignal("Lorem ipsum dolor sit amet");
return (
<form>
<div>
<label>
Name
<input
type="text"
value={name()}
onInput={(e) => setName(e.target.value)}
/>
</label>
</div>
<div>
<label>
Active
<input
type="checkbox"
checked={isActive()}
onChange={(e) => setIsActive(e.target.checked)}
/>
</label>
</div>
<div>
<label>
Gender
<select
value={gender()}
onChange={(e) => setGender(e.target.value)}
>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
</label>
</div>
<div>
Level
<label>
<input
type="radio"
name="rating"
value={1}
checked={rating() === 1}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
1
</label>
<label>
<input
type="radio"
name="rating"
value={2}
checked={rating() === 2}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
2
</label>
<label>
<input
type="radio"
name="rating"
value={3}
checked={rating() === 3}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
3
</label>
<label>
<input
type="radio"
name="rating"
value={4}
checked={rating() === 4}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
4
</label>
<label>
<input
type="radio"
name="rating"
value={5}
checked={rating() === 5}
onChange={(e) => setRating(+e.target.value)}
/>{" "}
5
</label>
</div>
<div>
<label>
Notes
<textarea
value={notes()}
onInput={(e) => setNotes(e.target.value)}
/>
</label>
</div>
</form>
);
}
```
## State Management
The following defines components `CountDisplay`, `CountIncrement` and `CountDecrement` that use a shared state `count`.
Note that these are implemented using only the core library of each framework, i.e. without [Redux](https://redux.js.org/) for React or [Vuex](https://next.vuex.vuejs.org/) for Vue.
For examples on using external libraries for state management, refer to the following code samples:
- [React State Demo](https://codesandbox.io/s/react-state-demo-swwb5)
- [Vue State Demo](https://codesandbox.io/s/vue-state-demo-3g87o)
### React
```jsx=
import { createContext, useContext, useReducer } from "react";
const CountContext = createContext({ count: 0, dispatch: null });
const useCount = () => useContext(CountContext);
const countReducer = (state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
throw new Error("Invalid action type");
}
};
const CountProvider = ({ children }) => {
const [count, dispatch] = useReducer(countReducer, 0);
return (
<CountContext.Provider value={{ count, dispatch }}>
{children}
</CountContext.Provider>
);
};
const CountDisplay = () => {
const { count } = useCount();
return <p>Count: {count}</p>;
};
const CountIncrement = () => {
const { dispatch } = useCount();
return (
<button onClick={() => dispatch({ type: "increment" })}>
Increment
</button>
);
};
const CountDecrement = () => {
const { dispatch } = useCount();
return (
<button onClick={() => dispatch({ type: "decrement" })}>
Decrement
</button>
);
};
export default function App() {
return (
<CountProvider>
<CountDisplay />
<CountIncrement />
<CountDecrement />
</CountProvider>
);
}
```
### Vue 3
```jsx=
// store.js
import { reactive, readonly } from "vue";
const state = reactive({
count: 0
});
const actions = {
increment: () => state.count++,
decrement: () => state.count--
};
export const countStore = {
state: readonly(state),
...actions
};
```
```jsx=
<!-- CountDisplay.vue -->
<template>
<p>Count: {{ count }}</p>
</template>
<script setup>
import { computed, inject } from "vue";
const { state } = inject("countStore");
const count = computed(() => state.count);
</script>
```
```jsx=
<!-- CountIncrement.vue -->
<template>
<button @click="increment">
Increment
</button>
</template>
<script setup>
import { inject } from "vue";
const { increment } = inject("countStore");
</script>
```
```jsx=
<!-- CountDecrement.vue -->
<template>
<button @click="decrement">
Decrement
</button>
</template>
<script setup>
import { inject } from "vue";
const { decrement } = inject("countStore");
</script>
```
```jsx=
<!-- App.vue -->
<template>
<CountDisplay />
<CountIncrement />
<CountDecrement />
</template>
<script setup>
import { provide } from "vue";
import { countStore } from "./store.js";
import CountDisplay from "./CountDisplay.vue";
import CountIncrement from "./CountIncrement.vue";
import CountDecrement from "./CountDecrement.vue";
provide("countStore", countStore);
</script>
```
### Svelte
```jsx=
// store.js
import { writable } from "svelte/store";
const createCountStore = () => {
const { subscribe, update } = writable(0);
return {
subscribe,
increment: () => update(count => count + 1),
decrement: () => update(count => count - 1)
}
}
export const count = createCountStore();
```
```jsx=
<!-- CountDisplay.svelte -->
<script>
import { count } from "./store";
</script>
<p>Count: {$count}</p>
```
```jsx=
<!-- CountIncrement.svelte -->
<script>
import { count } from "./store";
</script>
<button on:click={() => count.increment()}>
Increment
</button>
```
```jsx=
<!-- CountDecrement.svelte -->
<script>
import { count } from "./store";
</script>
<button on:click={() => count.decrement()}>
Decrement
</button>
```
```jsx=
<!-- App.svelte -->
<script>
import CountDisplay from "./CountDisplay.svelte";
import CountIncrement from "./CountIncrement.svelte";
import CountDecrement from "./CountDecrement.svelte";
</script>
<CountDisplay />
<CountIncrement />
<CountDecrement />
```
### SolidJS
```jsx=
import { render } from "solid-js/web";
import { createStore, produce } from "solid-js/store";
const createCountStore = () => {
const [state, setState] = createStore({ count: 0 });
return {
state: state,
increment: () => setState(({ count }) => ({ count: count + 1})),
// ...can also be written as
// increment: () => setState("count", (count) => count + 1),
decrement: () => setState(({ count }) => ({ count: count - 1}))
// ...can also be written as
// decrement: () => setState("count", (count) => count - 1)
}
}
const store = createCountStore();
const CountDisplay = () => {
return <p>Count: {store.state.count}</p>;
};
const CountIncrement = () => {
return (
<button onClick={() => store.increment()}>
Increment
</button>
);
};
const CountDecrement = () => {
return (
<button onClick={() => store.decrement()}>
Decrement
</button>
);
};
export default function App() {
return (
<>
<CountDisplay />
<CountIncrement />
<CountDecrement />
</>
);
}
```