# 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 /> </> ); } ```