Aufbau einer Full-Stack DevJokes-App mit TanStack Start

Dieses Tutorial führt Sie durch den Aufbau einer vollständigen Full-Stack-Anwendung mit TanStack Start. Sie erstellen eine DevJokes-App, in der Benutzer entwicklerbezogene Witze anzeigen und hinzufügen können, und demonstrieren dabei Schlüsselkonzepte von TanStack Start, einschließlich Serverfunktionen, dateibasiertem Datenspeicher und React-Komponenten.

Hier ist eine Demo der App in Aktion

Der vollständige Code für dieses Tutorial ist auf GitHub verfügbar.

Was Sie lernen werden

  1. Einrichten eines TanStack Start-Projekts
  2. Implementierung von Serverfunktionen
  3. Daten aus Dateien lesen und in Dateien schreiben
  4. Aufbau einer vollständigen UI mit React-Komponenten
  5. Verwendung von TanStack Router für Datenabruf und Navigation

Voraussetzungen

  • Grundkenntnisse in React und TypeScript.
  • Node.js und pnpm auf Ihrem Rechner installiert

Gut zu wissen

Einrichten eines TanStack Start-Projekts

Lassen Sie uns zuerst ein neues TanStack Start-Projekt erstellen

bash
pnpx create-start-app devjokes
cd devjokes
pnpx create-start-app devjokes
cd devjokes

Wenn dieses Skript ausgeführt wird, werden Ihnen einige Setup-Fragen gestellt. Sie können die Optionen auswählen, die für Sie passen, oder einfach Enter drücken, um die Standardeinstellungen zu übernehmen.

Optional können Sie ein --add-on Flag übergeben, um Optionen wie Shadcn, Clerk, Convex, TanStack Query usw. zu erhalten.

Nach Abschluss des Setups, installieren Sie die Abhängigkeiten und starten Sie den Entwicklungsserver

bash
pnpm i
pnpm dev
pnpm i
pnpm dev

Für dieses Projekt benötigen wir ein paar zusätzliche Pakete

bash
# Install uuid for generating unique IDs
pnpm add uuid
pnpm add -D @types/uuid
# Install uuid for generating unique IDs
pnpm add uuid
pnpm add -D @types/uuid

Verständnis der Projektstruktur

An diesem Punkt sollte die Projektstruktur wie folgt aussehen -

/devjokes
├── src/
│   ├── routes/
│   │   ├── __root.tsx                    # Root layout
│   │   ├── index.tsx                     # Home page
│   │   ├── demo.start.server-funcs.tsx   # Demo server functions
│   │   └── demo.start.api-request.tsx    # Demo API request
│   ├── api/                              # API endpoints
│   ├── components/                       # React components
│   ├── api.ts                            # API handler.
│   ├── client.tsx                        # Client entry point
│   ├── router.tsx                        # Router configuration
│   ├── routeTree.gen.ts                  # Generated route tree
│   ├── ssr.tsx                           # Server-side rendering
│   └── styles.css                        # Global styles
├── public/                               # Static assets
├── app.config.ts                         # TanStack Start configuration
├── package.json                          # Project dependencies
└── tsconfig.json                         # TypeScript configuration
/devjokes
├── src/
│   ├── routes/
│   │   ├── __root.tsx                    # Root layout
│   │   ├── index.tsx                     # Home page
│   │   ├── demo.start.server-funcs.tsx   # Demo server functions
│   │   └── demo.start.api-request.tsx    # Demo API request
│   ├── api/                              # API endpoints
│   ├── components/                       # React components
│   ├── api.ts                            # API handler.
│   ├── client.tsx                        # Client entry point
│   ├── router.tsx                        # Router configuration
│   ├── routeTree.gen.ts                  # Generated route tree
│   ├── ssr.tsx                           # Server-side rendering
│   └── styles.css                        # Global styles
├── public/                               # Static assets
├── app.config.ts                         # TanStack Start configuration
├── package.json                          # Project dependencies
└── tsconfig.json                         # TypeScript configuration

Diese Struktur mag auf den ersten Blick überwältigend erscheinen, aber hier sind die wichtigsten Dateien, auf die Sie sich konzentrieren müssen

  1. router.tsx - Konfiguriert das Routing für Ihre Anwendung
  2. src/routes/__root.tsx - Die Stamm-Layoutkomponente, in der Sie globale Stile und Komponenten hinzufügen können
  3. src/routes/index.tsx - Ihre Startseite
  4. client.tsx - Clientseitiger Einstiegspunkt
  5. ssr.tsx - Behandelt Server Side Rendering

Sobald Ihr Projekt eingerichtet ist, können Sie Ihre App unter localhost:3000 aufrufen. Sie sollten die Standard-TanStack Start-Willkommensseite sehen.

An diesem Punkt wird Ihre App wie folgt aussehen -

TanStack Start Welcome Page After Setup

Schritt 1: Daten aus einer Datei lesen

Beginnen wir damit, ein dateibasiertes Speichersystem für unsere Witze zu erstellen.

Schritt 1.1: JSON-Datei mit Witzen erstellen

Lassen Sie uns eine Liste von Witzen einrichten, die wir auf der Seite darstellen können. Erstellen Sie ein data-Verzeichnis in Ihrem Projektstammverzeichnis und eine jokes.json-Datei darin

bash
mkdir -p src/data
touch src/data/jokes.json
mkdir -p src/data
touch src/data/jokes.json

Fügen wir nun einige Beispielwitze in diese Datei ein

json
[
  {
    "id": "1",
    "question": "Why don't keyboards sleep?",
    "answer": "Because they have two shifts"
  },
  {
    "id": "2",
    "question": "Are you a RESTful API?",
    "answer": "Because you GET my attention, PUT some love, POST the cutest smile, and DELETE my bad day"
  },
  {
    "id": "3",
    "question": "I used to know a joke about Java",
    "answer": "But I ran out of memory."
  },
  {
    "id": "4",
    "question": "Why do Front-End Developers eat lunch alone?",
    "answer": "Because, they don't know how to join tables."
  },
  {
    "id": "5",
    "question": "I am declaring a war.",
    "answer": "var war;"
  }
]
[
  {
    "id": "1",
    "question": "Why don't keyboards sleep?",
    "answer": "Because they have two shifts"
  },
  {
    "id": "2",
    "question": "Are you a RESTful API?",
    "answer": "Because you GET my attention, PUT some love, POST the cutest smile, and DELETE my bad day"
  },
  {
    "id": "3",
    "question": "I used to know a joke about Java",
    "answer": "But I ran out of memory."
  },
  {
    "id": "4",
    "question": "Why do Front-End Developers eat lunch alone?",
    "answer": "Because, they don't know how to join tables."
  },
  {
    "id": "5",
    "question": "I am declaring a war.",
    "answer": "var war;"
  }
]

Schritt 1.2: Typen für unsere Daten erstellen

Lassen Sie uns eine Datei erstellen, um unsere Datentypen zu definieren. Erstellen Sie eine neue Datei unter src/types/index.ts

typescript
// src/types/index.ts
export interface Joke {
  id: string
  question: string
  answer: string
}

export type JokesData = Joke[]
// src/types/index.ts
export interface Joke {
  id: string
  question: string
  answer: string
}

export type JokesData = Joke[]

Schritt 1.3: Serverfunktionen zum Lesen der Datei erstellen

Lassen Sie uns eine neue Datei src/serverActions/jokesActions.ts erstellen, um eine Serverfunktion für Lese-Schreib-Vorgänge zu erstellen. Wir werden eine Serverfunktion mit createServerFn erstellen.

tsx
// src/serverActions/jokesActions.ts

import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import type { JokesData } from '../types'

const JOKES_FILE = 'src/data/jokes.json'

export const getJokes = createServerFn({ method: 'GET' }).handler(async () => {
  const jokes = await fs.promises.readFile(JOKES_FILE, 'utf-8')
  return JSON.parse(jokes) as JokesData
})
// src/serverActions/jokesActions.ts

import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import type { JokesData } from '../types'

const JOKES_FILE = 'src/data/jokes.json'

export const getJokes = createServerFn({ method: 'GET' }).handler(async () => {
  const jokes = await fs.promises.readFile(JOKES_FILE, 'utf-8')
  return JSON.parse(jokes) as JokesData
})

In diesem Code verwenden wir createServerFn, um eine Serverfunktion zu erstellen, die Witze aus der JSON-Datei liest. Die Funktion handler ist dort, wo wir das fs-Modul verwenden, um die Datei zu lesen.

Schritt 1.4: Serverfunktion auf der Client-Seite nutzen

Um diese Serverfunktion zu nutzen, können wir sie einfach in unserem Code aufrufen, indem wir TanStack Router verwenden, der bereits mit TanStack Start geliefert wird!

Lassen Sie uns nun eine neue Komponente JokesList erstellen, um die Witze auf der Seite mit einem Hauch von Tailwind-Styling darzustellen.

tsx
// src/components/JokesList.tsx
import { Joke } from '../types'

interface JokesListProps {
  jokes: Joke[]
}

export function JokesList({ jokes }: JokesListProps) {
  if (!jokes || jokes.length === 0) {
    return <p className="text-gray-500 italic">No jokes found. Add some!</p>
  }

  return (
    <div className="space-y-4">
      <h2 className="text-xl font-semibold">Jokes Collection</h2>
      {jokes.map((joke) => (
        <div
          key={joke.id}
          className="bg-white p-4 rounded-lg shadow-md border border-gray-200"
        >
          <p className="font-bold text-lg mb-2">{joke.question}</p>
          <p className="text-gray-700">{joke.answer}</p>
        </div>
      ))}
    </div>
  )
}
// src/components/JokesList.tsx
import { Joke } from '../types'

interface JokesListProps {
  jokes: Joke[]
}

export function JokesList({ jokes }: JokesListProps) {
  if (!jokes || jokes.length === 0) {
    return <p className="text-gray-500 italic">No jokes found. Add some!</p>
  }

  return (
    <div className="space-y-4">
      <h2 className="text-xl font-semibold">Jokes Collection</h2>
      {jokes.map((joke) => (
        <div
          key={joke.id}
          className="bg-white p-4 rounded-lg shadow-md border border-gray-200"
        >
          <p className="font-bold text-lg mb-2">{joke.question}</p>
          <p className="text-gray-700">{joke.answer}</p>
        </div>
      ))}
    </div>
  )
}

Rufen wir nun unsere Serverfunktion in App.jsx mit TanStack Router auf, der bereits mit TanStack Start geliefert wird!

jsx
// App.jsx
import { createFileRoute } from '@tanstack/react-router'
import { getJokes } from './serverActions/jokesActions'
import { JokesList } from './JokesList'

export const Route = createFileRoute('/')({
  loader: async () => {
    // Load jokes data when the route is accessed
    return getJokes()
  },
  component: App,
})

const App = () => {
  const jokes = Route.useLoaderData() || []

  return (
    <div className="p-4 flex flex-col">
      <h1 className="text-2xl">DevJokes</h1>
      <JokesList jokes={jokes} />
    </div>
  )
}
// App.jsx
import { createFileRoute } from '@tanstack/react-router'
import { getJokes } from './serverActions/jokesActions'
import { JokesList } from './JokesList'

export const Route = createFileRoute('/')({
  loader: async () => {
    // Load jokes data when the route is accessed
    return getJokes()
  },
  component: App,
})

const App = () => {
  const jokes = Route.useLoaderData() || []

  return (
    <div className="p-4 flex flex-col">
      <h1 className="text-2xl">DevJokes</h1>
      <JokesList jokes={jokes} />
    </div>
  )
}

Wenn die Seite geladen wird, enthält jokes bereits Daten aus der jokes.json-Datei!

Mit etwas Tailwind-Styling sollte die App wie folgt aussehen

DevJoke App with 5 DevJokes

Schritt 2: Daten in eine Datei schreiben

Bisher konnten wir erfolgreich aus der Datei lesen! Wir können denselben Ansatz verwenden, um in die jokes.json-Datei zu schreiben, indem wir createServerFunction verwenden.

Schritt 2.1: Serverfunktion zum Schreiben in die Datei erstellen

Es ist an der Zeit, die jokes.json-Datei zu ändern, damit wir neue Witze hinzufügen können. Lassen Sie uns eine weitere Serverfunktion erstellen, diesmal jedoch mit einer POST-Methode, um in dieselbe Datei zu schreiben.

tsx
// src/serverActions/jokesActions.ts
import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import { v4 as uuidv4 } from 'uuid' // Add this import
import type { Joke, JokesData } from '../types'

export const addJoke = createServerFn({ method: 'POST' })
  .validator((data: { question: string; answer: string }) => {
    // Validate input data
    if (!data.question || !data.question.trim()) {
      throw new Error('Joke question is required')
    }
    if (!data.answer || !data.answer.trim()) {
      throw new Error('Joke answer is required')
    }
    return data
  })
  .handler(async ({ data }) => {
    try {
      // Read the existing jokes from the file
      const jokesData = await getJokes()

      // Create a new joke with a unique ID
      const newJoke: Joke = {
        id: uuidv4(),
        question: data.question,
        answer: data.answer,
      }

      // Add the new joke to the list
      const updatedJokes = [...jokesData, newJoke]

      // Write the updated jokes back to the file
      await fs.promises.writeFile(
        JOKES_FILE,
        JSON.stringify(updatedJokes, null, 2),
        'utf-8',
      )

      return newJoke
    } catch (error) {
      console.error('Failed to add joke:', error)
      throw new Error('Failed to add joke')
    }
  })
// src/serverActions/jokesActions.ts
import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import { v4 as uuidv4 } from 'uuid' // Add this import
import type { Joke, JokesData } from '../types'

export const addJoke = createServerFn({ method: 'POST' })
  .validator((data: { question: string; answer: string }) => {
    // Validate input data
    if (!data.question || !data.question.trim()) {
      throw new Error('Joke question is required')
    }
    if (!data.answer || !data.answer.trim()) {
      throw new Error('Joke answer is required')
    }
    return data
  })
  .handler(async ({ data }) => {
    try {
      // Read the existing jokes from the file
      const jokesData = await getJokes()

      // Create a new joke with a unique ID
      const newJoke: Joke = {
        id: uuidv4(),
        question: data.question,
        answer: data.answer,
      }

      // Add the new joke to the list
      const updatedJokes = [...jokesData, newJoke]

      // Write the updated jokes back to the file
      await fs.promises.writeFile(
        JOKES_FILE,
        JSON.stringify(updatedJokes, null, 2),
        'utf-8',
      )

      return newJoke
    } catch (error) {
      console.error('Failed to add joke:', error)
      throw new Error('Failed to add joke')
    }
  })

In diesem Code

  • Wir verwenden createServerFn, um Serverfunktionen zu erstellen, die auf dem Server laufen, aber vom Client aufgerufen werden können. Diese Serverfunktion dient zum Schreiben von Daten in die Datei.
  • Wir werden zuerst validator verwenden, um die Eingabedaten zu validieren. Dies ist eine gute Praxis, um sicherzustellen, dass die von uns empfangenen Daten das richtige Format haben.
  • Wir werden den eigentlichen Schreibvorgang in der Funktion handler durchführen.
  • getJokes liest die Witze aus unserer JSON-Datei.
  • addJoke validiert die Eingabedaten und fügt einen neuen Witz zu unserer Datei hinzu.
  • Wir verwenden uuidv4(), um eindeutige IDs für unsere Witze zu generieren.

Schritt 2.2: Formular zum Hinzufügen von Witzen zu unserer JSON-Datei hinzufügen

Ändern wir nun unsere Startseite, um Witze anzuzeigen und ein Formular zum Hinzufügen neuer Witze bereitzustellen. Erstellen wir eine neue Komponente namens JokeForm.jsx und fügen Sie das folgende Formular hinzu

tsx
// src/components/JokeForm.tsx
import { useState } from 'react'
import { useRouter } from '@tanstack/react-router'
import { addJoke } from '../serverActions/jokesActions'

export function JokeForm() {
  const router = useRouter()
  const [question, setQuestion] = useState('')
  const [answer, setAnswer] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  return (
    <form onSubmit={handleSubmit} className="flex flex-row gap-2 mb-6">
      {error && (
        <div className="bg-red-100 text-red-700 p-2 rounded mb-4">{error}</div>
      )}

      <div className="flex flex-col sm:flex-row gap-4 mb-8">
        <input
          id="question"
          type="text"
          placeholder="Enter joke question"
          className="w-full p-2 border rounded focus:ring focus:ring-blue-300 flex-1"
          value={question}
          onChange={(e) => setQuestion(e.target.value)}
          required
        />

        <input
          id="answer"
          type="text"
          placeholder="Enter joke answer"
          className="w-full p-2 border rounded focus:ring focus:ring-blue-300 flex-1 py-4"
          value={answer}
          onChange={(e) => setAnswer(e.target.value)}
          required
        />

        <button
          type="submit"
          disabled={isSubmitting}
          className="bg-blue-500 hover:bg-blue-600 text-white font-medium rounded disabled:opacity-50 px-4"
        >
          {isSubmitting ? 'Adding...' : 'Add Joke'}
        </button>
      </div>
    </form>
  )
}
// src/components/JokeForm.tsx
import { useState } from 'react'
import { useRouter } from '@tanstack/react-router'
import { addJoke } from '../serverActions/jokesActions'

export function JokeForm() {
  const router = useRouter()
  const [question, setQuestion] = useState('')
  const [answer, setAnswer] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  return (
    <form onSubmit={handleSubmit} className="flex flex-row gap-2 mb-6">
      {error && (
        <div className="bg-red-100 text-red-700 p-2 rounded mb-4">{error}</div>
      )}

      <div className="flex flex-col sm:flex-row gap-4 mb-8">
        <input
          id="question"
          type="text"
          placeholder="Enter joke question"
          className="w-full p-2 border rounded focus:ring focus:ring-blue-300 flex-1"
          value={question}
          onChange={(e) => setQuestion(e.target.value)}
          required
        />

        <input
          id="answer"
          type="text"
          placeholder="Enter joke answer"
          className="w-full p-2 border rounded focus:ring focus:ring-blue-300 flex-1 py-4"
          value={answer}
          onChange={(e) => setAnswer(e.target.value)}
          required
        />

        <button
          type="submit"
          disabled={isSubmitting}
          className="bg-blue-500 hover:bg-blue-600 text-white font-medium rounded disabled:opacity-50 px-4"
        >
          {isSubmitting ? 'Adding...' : 'Add Joke'}
        </button>
      </div>
    </form>
  )
}

Schritt 2.3: Formular mit der Serverfunktion verbinden

Verbinden wir nun das Formular mit unserer addJoke Serverfunktion in der Funktion handleSubmit. Das Aufrufen einer Serveraktion ist einfach! Es ist nur ein Funktionsaufruf.

tsx
//JokeForm.tsx
import { useState } from 'react'
import { addJoke } from '../serverActions/jokesActions'
import { useRouter } from '@tanstack/react-router'

export function JokeForm() {
  const router = useRouter()
  const [question, setQuestion] = useState('')
  const [answer, setAnswer] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const handleSubmit = async () => {
    if (!question || !answer || isSubmitting) return
    try {
      setIsSubmitting(true)
      await addJoke({
        data: { question, answer },
      })

      // Clear form
      setQuestion('')
      setAnswer('')

      // Refresh data
      router.invalidate()
    } catch (error) {
      console.error('Failed to add joke:', error)
      setError('Failed to add joke')
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-row gap-2 mb-6">
      {error && (
        <div className="bg-red-100 text-red-700 p-2 rounded mb-4">{error}</div>
      )}
      <input
        type="text"
        name="question"
        placeholder="Question"
        className="p-1 border rounded w-full"
        required
        onChange={(e) => setQuestion(e.target.value)}
        value={question}
      />
      <input
        type="text"
        name="answer"
        placeholder="Answer"
        className="p-1 border rounded w-full"
        required
        onChange={(e) => setAnswer(e.target.value)}
        value={answer}
      />
      <button
        className="bg-blue-500 text-white p-1 rounded hover:bg-blue-600"
        disabled={isSubmitting}
      >
        {isSubmitting ? 'Adding...' : 'Add Joke'}
      </button>
    </form>
  )
}
//JokeForm.tsx
import { useState } from 'react'
import { addJoke } from '../serverActions/jokesActions'
import { useRouter } from '@tanstack/react-router'

export function JokeForm() {
  const router = useRouter()
  const [question, setQuestion] = useState('')
  const [answer, setAnswer] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const handleSubmit = async () => {
    if (!question || !answer || isSubmitting) return
    try {
      setIsSubmitting(true)
      await addJoke({
        data: { question, answer },
      })

      // Clear form
      setQuestion('')
      setAnswer('')

      // Refresh data
      router.invalidate()
    } catch (error) {
      console.error('Failed to add joke:', error)
      setError('Failed to add joke')
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <form onSubmit={handleSubmit} className="flex flex-row gap-2 mb-6">
      {error && (
        <div className="bg-red-100 text-red-700 p-2 rounded mb-4">{error}</div>
      )}
      <input
        type="text"
        name="question"
        placeholder="Question"
        className="p-1 border rounded w-full"
        required
        onChange={(e) => setQuestion(e.target.value)}
        value={question}
      />
      <input
        type="text"
        name="answer"
        placeholder="Answer"
        className="p-1 border rounded w-full"
        required
        onChange={(e) => setAnswer(e.target.value)}
        value={answer}
      />
      <button
        className="bg-blue-500 text-white p-1 rounded hover:bg-blue-600"
        disabled={isSubmitting}
      >
        {isSubmitting ? 'Adding...' : 'Add Joke'}
      </button>
    </form>
  )
}

Damit sollte unsere UI wie folgt aussehen: DevJoke App mit Formular zum Hinzufügen von Witzen

Verständnis, wie alles zusammenarbeitet

Lassen Sie uns aufschlüsseln, wie die verschiedenen Teile unserer Anwendung zusammenarbeiten

  1. Serverfunktionen: Diese laufen auf dem Server und verwalten Datenoperationen

    • getJokes: Liest die Witze aus unserer JSON-Datei
    • addJoke: Fügt einen neuen Witz zu unserer JSON-Datei hinzu
  2. TanStack Router: Verwaltet Routing und Datenladung

    • Die Loader-Funktion ruft die Witze-Daten ab, wenn auf die Route zugegriffen wird
    • useLoaderData macht diese Daten in unserer Komponente verfügbar
    • router.invalidate() aktualisiert die Daten, wenn wir einen neuen Witz hinzufügen
  3. React-Komponenten: Bauen die Benutzeroberfläche unserer Anwendung auf

    • JokesList: Zeigt die Liste der Witze an
    • JokeForm: Bietet ein Formular zum Hinzufügen neuer Witze
  4. Dateibasiertes Speichersystem: Speichert unsere Witze in einer JSON-Datei

    • Das Lesen und Schreiben wird vom Node.js fs-Modul übernommen
    • Daten bleiben auch nach Serverneustarts erhalten

Wie Daten durch die Anwendung fließen

Datenfluss

Data Flow Diagram

Wenn ein Benutzer die Startseite besucht

  1. Die loader-Funktion in der Route ruft die Serverfunktion getJokes() auf
  2. Der Server liest jokes.json und gibt die Witze-Daten zurück
  3. Diese Daten werden über useLoaderData() an die Komponente HomePage übergeben
  4. Die Komponente HomePage gibt die Daten an die Komponente JokesList weiter

Wenn ein Benutzer einen neuen Witz hinzufügt

  1. Er füllt das Formular aus und sendet es ab
  2. Die Funktion handleSubmit ruft die Serverfunktion addJoke() auf
  3. Der Server liest die aktuellen Witze, fügt den neuen Witz hinzu und schreibt die aktualisierten Daten zurück in jokes.json
  4. Nach Abschluss des Vorgangs rufen wir router.invalidate() auf, um die Daten zu aktualisieren
  5. Dies löst erneut den Loader aus, der die aktualisierten Witze abruft
  6. Die Benutzeroberfläche wird aktualisiert, um den neuen Witz in der Liste anzuzeigen

Hier ist eine Demo der App in Aktion

Häufige Probleme und Debugging

Hier sind einige häufige Probleme, auf die Sie beim Erstellen Ihrer TanStack Start-Anwendung stoßen könnten, und wie Sie sie lösen können

Serverfunktionen funktionieren nicht

Wenn Ihre Serverfunktionen nicht wie erwartet funktionieren

  1. Überprüfen Sie, ob Sie die richtige HTTP-Methode verwenden (GET, POST, etc.)
  2. Stellen Sie sicher, dass die Dateipfade korrekt und für den Server zugänglich sind
  3. Überprüfen Sie die Serverkonsole auf Fehlermeldungen
  4. Stellen Sie sicher, dass Sie keine clientseitigen APIs in Serverfunktionen verwenden

Routendaten werden nicht geladen

Wenn Routendaten nicht korrekt geladen werden

  1. Überprüfen Sie, ob Ihre Loader-Funktion korrekt implementiert ist
  2. Prüfen Sie, ob Sie useLoaderData() korrekt verwenden
  3. Suchen Sie nach Fehlern in der Browserkonsole
  4. Stellen Sie sicher, dass Ihre Serverfunktion korrekt funktioniert

Probleme bei der Formularübermittlung

Wenn Formularübermittlungen nicht funktionieren

  1. Prüfen Sie auf Validierungsfehler in Ihrer Serverfunktion
  2. Stellen Sie sicher, dass die Verhinderung von Formularereignissen (e.preventDefault()) funktioniert
  3. Stellen Sie sicher, dass Zustandsaktualisierungen korrekt erfolgen
  4. Suchen Sie nach Netzwerkfehlern in den Entwicklertools des Browsers

Probleme beim Lesen/Schreiben von Dateien

Beim Arbeiten mit dateibasiertem Speicher

  1. Stellen Sie sicher, dass die Dateipfade korrekt sind
  2. Überprüfen Sie die Dateiberechtigungen
  3. Stellen Sie sicher, dass Sie asynchrone Operationen ordnungsgemäß mit await behandeln
  4. Fügen Sie eine ordnungsgemäße Fehlerbehandlung für Dateioperationen hinzu

Fazit

Herzlichen Glückwunsch! Sie haben eine Full-Stack DevJokes-App mit TanStack Start erstellt. In diesem Tutorial haben Sie gelernt

  • Wie man ein TanStack Start-Projekt einrichtet
  • Wie man Serverfunktionen für Datenoperationen implementiert
  • Wie man Daten aus Dateien liest und in Dateien schreibt
  • Wie man React-Komponenten für Ihre Benutzeroberfläche erstellt
  • Wie man TanStack Router für Routing und Datenabruf verwendet

Diese einfache Anwendung demonstriert die Leistungsfähigkeit von TanStack Start zum Erstellen von Full-Stack-Anwendungen mit minimalem Code. Sie können diese App erweitern, indem Sie Funktionen hinzufügen wie

  • Witz-Kategorien
  • Möglichkeit, Witze zu bearbeiten und zu löschen
  • Benutzerauthentifizierung
  • Abstimmung für Lieblingswitze

Der vollständige Code für dieses Tutorial ist auf GitHub verfügbar.

Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Prisma
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.