React開発者のためのSvelte 5入門講座　第一章

## Introduction
こんにちわ、milkyskiesだよ♪
Svelte 5の講座を始めるよ♪
React厨の皆様ならば１時間で覚えられるね♪

### 使われる技術
React
- NextJS (App Router）
- TypeScript
- Tailwind CSS

Svelte 5
- SvelteKit
- TypeScript
- Tailwind CSS

## State

### Stateの基本
React|Svelte 5
---|---
`useState`|`$state`

**React**
```tsx:src/app/page.tsx
import React, { useState } from "react";

export default function Page() {
    const [name, setName] = useState("太郎");

    return (
        <p>
            こんにちは、{name}！
        </p>
    );
}
```

**Svelte 5**
```svelte:src/routes/+page.svelte
<script lang="ts">
    let name = $state("太郎");
</script>

<p>
    こんにちは、 {name}！
</p>
```

---

### Stateの変更

ついでに`useEffect`のSvelte 5版も紹介するよ！

React|Svelte 5
---|---
`setName("次郎")`|`name = "次郎"`
`useEffect`|`$effect`

**React**
```tsx:src/app/page.tsx
import React, { useState, useEffect } from "react";

export default function Page() {
    const [name, setName] = useState("太郎");

    useEffect(() => {
        setName("次郎");
    }, []);

    return (
        <p>
            こんにちは、{name}！
        </p>
    );
}
```

**Svelte 5**
```svelte:src/routes/+page.svelte
<script lang="ts">
    let name = $state("太郎");

    $effect(() => {
        name = "次郎";
    });
</script>

<p>
    こんにちは、 {name}！
</p>
```

---

### ボタンでstateを変更

Svelteでは`onClick`じゃなくて`onclick`だよ！本来のHTMLと一緒だよ！

React|Svelte 5
---|---
`onClick`|`onclick`

**React**
```tsx:src/app/page.tsx
import React, { useState, useEffect } from "react";

export default function Page() {
    const [count, setCount] = useState(0);

    function increaseCount(amount: number) {
        setCount((count) => count + amount);
    }

    return (
        <>
            <p>count: {count}</p>
            <button onClick={() => increaseCount(1)}>countを増やす</button>
        </>
    );
}
```

**Svelte 5**
```svelte:src/routes/+page.svelte
<script lang="ts">
    let count = $state(0);

    function increaseCount(amount: number) {
        count += amount;
    }
</script>

<p>count: {count}</p>
<button onclick={() => increaseCount(1)}>countを増やす</button>
```

---

### 入力欄でリアルタイムでstateを更新
ここに関しては、ReactとSvelteがかなり違ってくる。
個人的にはSvelteのほうが圧倒的に書きやすいと思う。

React|Svelte 5
---|---
`onChange`|`bind`

**React**
```tsx:src/app/page.tsx
import React, { ChangeEvent, useState } from "react";

export default function Page() {
  const [name, setName] = useState("");

  function handleInputChange(event: ChangeEvent<HTMLInputElement>) {
      setName(event.target.value);
  }

  return (
      <>
          <input onChange={handleInputChange}>
          <p>こんにちは、{name}！</p>
      </>
  );
}
```

**Svelte 5**
```svelte:src/routes/+page.svelte
<script lang="ts">
    let name = $state("");
</script>

<input bind:value={name}>
<p>こんにちは、{name}！</p>
```


## Components

### Componentの作成

ついでにTailwindの使い方も少し紹介するよ！

React|Svelte 5
---|---
`className`|`class`

#### 別ファイルでcomponentを作成する方法
**React**
```tsx:src/components/card.tsx
type CardProps = {
    title: string
    text: string
};

export function Card({ title, text }: CardProps) {
  return (
    <div className="bg-slate-200 h-screen w-screen flex items-center justify-center">
        <div className="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
            <h2 className="text-2xl font-bold">{title}</h2>
            <p className="text-slate-500 mt-2">{text}</p>
        </div>
    </div>
  );
}
```

```tsx:src/app/page.tsx
import { Card } from '@/components/card';

export default function Page() {
  return (
    <Card title="カード" text="こんにちは！" />
  );
}
```

**Svelte 5**
```svelte:src/components/card.svelte
<script lang="ts">
    type CardProps = {
		title: string
		text: string
	};

	let { title, text }: CardProps = $props();
</script>

<div class="bg-slate-200 h-screen w-screen flex items-center justify-center">
    <div class="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
        <h2 class="text-2xl font-bold">{title}</h2>
        <p class="text-slate-500 mt-2">{text}</p>
    </div>
</div>
```

```svelte:src/routes/+page.svelte
<script lang="ts">
    import Card from '@components/card.svelte';
</script>

<Card title="カード" text="こんにちは！" />
```

---

#### 同じファイルでcomponentを作る方法

**React**

```tsx:src/app/page.tsx
type CardProps = {
    title: string
    text: string
};

function Card({ title, text }: CardProps) {
  return (
    <div className="bg-slate-200 h-screen w-screen flex items-center justify-center">
        <div className="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
            <h2 className="text-2xl font-bold">{title}</h2>
            <p className="text-slate-500 mt-2">{text}</p>
        </div>
    </div>
  );
}

export default function Page() {
  return (
    <Card title="カード" text="こんにちは！" />
  );
}
```

**Svelte 5**

こっちはあまりおすすめしないかも！Componentを作るときはだいたい上記みたいに**別ファイルで作りましょう**！

```svelte:src/routes/+page.svelte
<script lang="ts">
	type CardProps = {
		title: string;
		text: string;
	};
</script>

{#snippet card({ title, text }: CardProps)}
	<div class="bg-slate-200 h-screen w-screen flex items-center justify-center">
		<div class="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
			<h2 class="text-2xl font-bold">{title}</h2>
			<p class="text-slate-500 mt-2">{text}</p>
		</div>
	</div>
{/snippet}

<div class="bg-slate-2000 h-screen w-screen flex items-center justify-center">
	{@render card({ title: 'カード', text: 'こんにちは！' })}
</div>
```

:::note warn
現時点(2024-08-13)、Svelte 5のコードブロックはまだQiitaに完全対応されていないようなので、コードブロックにエラーが出ていますが、コード自体は大丈夫です。
:::

---

#### Componentにcomponentを渡す方法

Reactと同じく、一つの引数を`children`と名付けるとcomponentの中にcomponentを入れられるようになる！
`children`じゃなくてもいいけど、Reactと同じくその場合は他のpropと同じように渡さないといけなくなるから注意！

React|Svelte 5
---|---
`ReactNode`|`Snippet`

**React**

```tsx:src/components/card.tsx
import { ReactNode } from 'react';

type CardProps = {
    title: string
    children: ReactNode
};

export function Card({ title, children }: CardProps) {
  return (
    <div className="bg-slate-200 h-screen w-screen flex items-center justify-center">
        <div className="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
            <h2 className="text-2xl font-bold">{title}</h2>
            <div className="text-slate-500 mt-2">
              {children}
            </div>
        </div>
    </div>
  );
}
```

```tsx:src/app/page.tsx
import { Card } from '@/components/card';

export default function Page() {
  return (
    <Card title="カード">
        <p>こんにちは！</p>
    </Card>
  );
}
```

**Svelte 5**
```svelte:src/components/card.svelte
<script lang="ts">
	import type { Snippet } from 'svelte';

	type CardProps = {
		title: string;
		children: Snippet;
	};

	let { title, children }: CardProps = $props();
</script>

<div class="bg-slate-200 h-screen w-screen flex items-center justify-center">
	<div class="bg-white shadow-2xl rounded-lg w-96 min-h-32 p-4">
		<h2 class="text-2xl font-bold">{title}</h2>
		<p class="text-slate-500 mt-2">
			{@render children()}
		</p>
	</div>
</div>
```

```svelte:src/routes/+page.svelte
<script lang="ts">
    import Card from '@components/card.svelte';
</script>

<Card title="カード">
    <p>こんにちは！</p>
</Card>
```

---

#### HTMLElementを継承してComponentを作る方法

**React**

```tsx:src/components/button.tsx
import { ButtonHTMLAttributes } from 'react';

type ButtonProps = {
  label: string;
  className?: string;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;

export const Button = ({ label, className, ...restProps }: ButtonProps) => {
  return (
    <button type="button" className={`bg-blue-500 text-white px-4 py-2 rounded-md ${className}`} {...restProps}>
      {label}
    </button>
  );
};
```

**Svelte 5**
```svelte:src/components/button.svelte
<script lang="ts">
	import type { HTMLButtonAttributes } from 'svelte/elements';

	type ButtonProps = {
		label: string;
		class?: string;
	} & HTMLButtonAttributes;

	let { label, class: className, ...restProps }: ButtonProps = $props();
</script>

<button class={`bg-blue-500 text-white px-4 py-2 rounded-md ${className}`} {...restProps}>
	{label}
</button>
```

:::note warn
現時点(2024-08-13)、Svelte 5のコードブロックはまだQiitaに完全に対応されていないようなので、コードブロックにエラーが出ていますが、コード自体は大丈夫です。
:::

## まとめ
いかがでしたか！Svelte 5はSvelte 4と比較すると、よりReactっぽい書き方になっている感じするね！

次回は、Svelteの条件分岐やループなどの紹介もしていきたいと思います。

ほな！