Elysia Tools

Explore ferramentas, exemplos, explicações visuais e guias temáticos em um espaço claro.

Categorias populares

Temas

Idioma
ZHESPTRUFRDEEN

Elysia Tools

Navegação

Busca de dados

Exemplos de TanStack Query

Exemplos de TanStack Query (anteriormente React Query) incluindo busca de dados, cache, mutações, atualizações otimistas e padrões avançados

Exemplos

Entradas desta coleção

CódigotypescriptSimples

Uso Básico do TanStack Query

Padrões fundamentais do TanStack Query incluindo consultas, mutações, tratamento de erros e estados de carregamento

Dificuldade

3/10

Tempo estimado

25 min

Etiquetas

tanstack-query, react, data-fetching, state-management

Pré-requisitos

React hooks, TypeScript, Async/await, REST APIs

typescript
// TanStack Query Basic Usage Examples
// TanStack Query is a powerful data fetching and state management library for React

import { useQuery, useMutation, useQueryClient, QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useState, useEffect, useCallback, useMemo } from 'react'

// 1. Setup Query Client
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      retry: 3,
      refetchOnWindowFocus: false,
    },
  },
})

// Provider component
export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UserProfile />
      <TodoList />
      <PostList />
    </QueryClientProvider>
  )
}

// 2. Basic Data Fetching with useQuery
interface User {
  id: number
  name: string
  email: string
  username: string
  address: {
    street: string
    city: string
    zipcode: string
  }
}

// Fetch function
const fetchUser = async (id: number): Promise<User> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)

  if (!response.ok) {
    throw new Error('Failed to fetch user')
  }

  return response.json()
}

// User component with useQuery
function UserProfile() {
  const [userId, setUserId] = useState(1)

  const {
    data: user,
    isLoading,
    isError,
    error,
    refetch,
    isFetching,
  } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    enabled: !!userId, // Only fetch when userId exists
    staleTime: 1000 * 60 * 10, // 10 minutes
  })

  const handleNextUser = () => {
    setUserId(prev => prev + 1)
  }

  const handlePrevUser = () => {
    setUserId(prev => Math.max(1, prev - 1))
  }

  return (
    <div className="p-6 border rounded-lg mb-6">
      <h2 className="text-2xl font-bold mb-4">User Profile</h2>

      <div className="mb-4 space-x-2">
        <button
          onClick={handlePrevUser}
          disabled={userId <= 1}
          className="px-3 py-1 bg-gray-500 text-white rounded disabled:opacity-50"
        >
          Previous
        </button>
        <span className="mx-2">User ID: {userId}</span>
        <button
          onClick={handleNextUser}
          className="px-3 py-1 bg-blue-500 text-white rounded"
        >
          Next
        </button>
        <button
          onClick={() => refetch()}
          disabled={isFetching}
          className="px-3 py-1 bg-green-500 text-white rounded disabled:opacity-50 ml-4"
        >
          {isFetching ? 'Refetching...' : 'Refetch'}
        </button>
      </div>

      {isLoading && <div className="text-blue-500">Loading user data...</div>}

      {isError && (
        <div className="text-red-500">
          Error: {error instanceof Error ? error.message : 'Unknown error'}
        </div>
      )}

      {user && (
        <div>
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">@{user.username}</p>
          <p className="text-gray-800">{user.email}</p>
          <p className="text-gray-700">
            {user.address.street}, {user.address.city} {user.address.zipcode}
          </p>
        </div>
      )}
    </div>
  )
}

// 3. Mutations with useMutation
interface Todo {
  id: number
  title: string
  completed: boolean
  userId: number
}

const fetchTodos = async (): Promise<Todo[]> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos')
  if (!response.ok) throw new Error('Failed to fetch todos')
  return response.json()
}

const createTodo = async (newTodo: { title: string; userId: number }): Promise<Todo> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      title: newTodo.title,
      userId: newTodo.userId,
      completed: false,
    }),
  })

  if (!response.ok) throw new Error('Failed to create todo')
  return response.json()
}

const toggleTodo = async (todoId: number, completed: boolean): Promise<Todo> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`, {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ completed }),
  })

  if (!response.ok) throw new Error('Failed to update todo')
  return response.json()
}

function TodoList() {
  const queryClient = useQueryClient()
  const [newTodoTitle, setNewTodoTitle] = useState('')

  // Fetch todos
  const {
    data: todos = [],
    isLoading,
    isError,
    error,
  } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    staleTime: 1000 * 60 * 5, // 5 minutes
  })

  // Create todo mutation
  const createTodoMutation = useMutation({
    mutationFn: createTodo,
    onSuccess: (newTodo) => {
      // Add new todo to cache
      queryClient.setQueryData(['todos'], (oldTodos: Todo[] = []) => [
        ...oldTodos,
        newTodo,
      ])
      setNewTodoTitle('') // Clear input
    },
    onError: (error) => {
      console.error('Failed to create todo:', error)
    },
  })

  // Toggle todo mutation
  const toggleTodoMutation = useMutation({
    mutationFn: ({ id, completed }: { id: number; completed: boolean }) =>
      toggleTodo(id, completed),
    onSuccess: (updatedTodo) => {
      // Update todo in cache
      queryClient.setQueryData(['todos'], (oldTodos: Todo[] = []) =>
        oldTodos.map(todo =>
          todo.id === updatedTodo.id ? updatedTodo : todo
        )
      )
    },
  })

  const handleCreateTodo = (e: React.FormEvent) => {
    e.preventDefault()
    if (newTodoTitle.trim()) {
      createTodoMutation.mutate({
        title: newTodoTitle.trim(),
        userId: 1, // Using fixed user ID for demo
      })
    }
  }

  const handleToggleTodo = (todoId: number, completed: boolean) => {
    toggleTodoMutation.mutate({ id: todoId, completed: !completed })
  }

  if (isLoading) return <div className="p-4">Loading todos...</div>
  if (isError) return <div className="p-4 text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>

  return (
    <div className="p-6 border rounded-lg mb-6">
      <h2 className="text-2xl font-bold mb-4">Todo List</h2>

      <form onSubmit={handleCreateTodo} className="mb-4 flex gap-2">
        <input
          type="text"
          value={newTodoTitle}
          onChange={(e) => setNewTodoTitle(e.target.value)}
          placeholder="New todo title..."
          className="flex-1 px-3 py-2 border rounded"
          disabled={createTodoMutation.isPending}
        />
        <button
          type="submit"
          disabled={createTodoMutation.isPending}
          className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {createTodoMutation.isPending ? 'Adding...' : 'Add Todo'}
        </button>
      </form>

      <ul className="space-y-2">
        {todos.slice(0, 10).map((todo) => (
          <li key={todo.id} className="flex items-center gap-2 p-2 border rounded">
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(todo.id, todo.completed)}
              disabled={toggleTodoMutation.isPending}
              className="w-4 h-4"
            />
            <span className={todo.completed ? 'line-through text-gray-500' : ''}>
              {todo.title}
            </span>
            {toggleTodoMutation.isPending && (
              <span className="text-sm text-gray-500">Updating...</span>
            )}
          </li>
        ))}
      </ul>
    </div>
  )
}

// 4. Dependent Queries
interface Post {
  id: number
  title: string
  body: string
  userId: number
}

interface Comment {
  id: number
  postId: number
  name: string
  email: string
  body: string
}

const fetchPosts = async (userId: number): Promise<Post[]> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`)
  if (!response.ok) throw new Error('Failed to fetch posts')
  return response.json()
}

const fetchComments = async (postId: number): Promise<Comment[]> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${postId}/comments`)
  if (!response.ok) throw new Error('Failed to fetch comments')
  return response.json()
}

function PostList() {
  const [selectedUserId] = useState(1)

  // First query: fetch posts for a user
  const {
    data: posts = [],
    isLoading: postsLoading,
    error: postsError,
  } = useQuery({
    queryKey: ['posts', selectedUserId],
    queryFn: () => fetchPosts(selectedUserId),
    enabled: !!selectedUserId,
  })

  // Second query: fetch comments for the first post (dependent on first query)
  const {
    data: comments = [],
    isLoading: commentsLoading,
    error: commentsError,
  } = useQuery({
    queryKey: ['comments', posts[0]?.id],
    queryFn: () => fetchComments(posts[0]?.id),
    enabled: !!posts[0]?.id, // Only run when we have a post ID
  })

  if (postsLoading) return <div className="p-4">Loading posts...</div>
  if (postsError) return <div className="p-4 text-red-500">Error loading posts</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Posts and Comments</h2>

      <div className="mb-6">
        <h3 className="text-xl font-semibold mb-3">User Posts</h3>
        <div className="space-y-3">
          {posts.slice(0, 3).map((post) => (
            <div key={post.id} className="p-3 border rounded">
              <h4 className="font-medium">{post.title}</h4>
              <p className="text-gray-600 text-sm mt-1">{post.body.slice(0, 100)}...</p>
            </div>
          ))}
        </div>
      </div>

      {commentsLoading ? (
        <div>Loading comments...</div>
      ) : commentsError ? (
        <div className="text-red-500">Error loading comments</div>
      ) : (
        <div>
          <h3 className="text-xl font-semibold mb-3">Comments for First Post</h3>
          <div className="space-y-2">
            {comments.slice(0, 3).map((comment) => (
              <div key={comment.id} className="p-3 bg-gray-50 rounded">
                <div className="font-medium">{comment.name}</div>
                <div className="text-sm text-gray-600">{comment.email}</div>
                <div className="text-sm mt-1">{comment.body}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  )
}

// 5. Pagination with Query Keys
function PaginatedComponent() {
  const [page, setPage] = useState(1)
  const pageSize = 10

  const {
    data,
    isLoading,
    isError,
    error,
  } = useQuery({
    queryKey: ['items', page, pageSize],
    queryFn: async () => {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/todos?_page=${page}&_limit=${pageSize}`
      )
      if (!response.ok) throw new Error('Failed to fetch data')
      return response.json()
    },
    keepPreviousData: true, // Keep previous data while loading new page
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Paginated Todos</h2>

      {isLoading && <div>Loading...</div>}
      {isError && <div className="text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>}

      {data && (
        <>
          <ul className="space-y-2 mb-4">
            {data.map((item: any) => (
              <li key={item.id} className="p-2 border rounded">
                {item.title}
              </li>
            ))}
          </ul>

          <div className="flex gap-2">
            <button
              onClick={() => setPage(prev => Math.max(1, prev - 1))}
              disabled={page === 1}
              className="px-3 py-1 bg-gray-500 text-white rounded disabled:opacity-50"
            >
              Previous
            </button>
            <span className="px-3 py-1">Page {page}</span>
            <button
              onClick={() => setPage(prev => prev + 1)}
              className="px-3 py-1 bg-blue-500 text-white rounded"
            >
              Next
            </button>
          </div>
        </>
      )}
    </div>
  )
}

export { App, UserProfile, TodoList, PostList, PaginatedComponent }
CódigotypescriptIntermediário

Padrões Avançados do TanStack Query

Padrões avançados do TanStack Query incluindo atualizações otimistas, consultas infinitas, recarga em segundo plano e hooks personalizados

Dificuldade

7/10

Tempo estimado

45 min

Etiquetas

tanstack-query, advanced, optimistic, infinite, realtime

Pré-requisitos

TanStack Query basics, React hooks, TypeScript, Advanced patterns

typescript
// TanStack Query Advanced Patterns
// Advanced data fetching patterns and optimizations

import {
  useQuery,
  useMutation,
  useQueryClient,
  useInfiniteQuery,
  QueryClient,
  QueryClientProvider,
  useQueries,
  QueryObserver,
} from '@tanstack/react-query'
import { useState, useEffect } from 'react'

// 1. Optimistic Updates
interface Todo {
  id: number
  title: string
  completed: boolean
  userId: number
  createdAt: string
}

const fetchTodos = async (): Promise<Todo[]> => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos')
  if (!response.ok) throw new Error('Failed to fetch todos')
  return response.json()
}

const updateTodo = async (id: number, updates: Partial<Todo>): Promise<Todo> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(updates),
  })
  if (!response.ok) throw new Error('Failed to update todo')
  return response.json()
}

const deleteTodo = async (id: number): Promise<void> => {
  const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {
    method: 'DELETE',
  })
  if (!response.ok) throw new Error('Failed to delete todo')
}

function OptimisticTodoList() {
  const queryClient = useQueryClient()

  const {
    data: todos = [],
    isLoading,
  } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
  })

  const updateTodoMutation = useMutation({
    mutationFn: ({ id, updates }: { id: number; updates: Partial<Todo> }) =>
      updateTodo(id, updates),
    onMutate: async ({ id, updates }) => {
      // Cancel any outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['todos'] })

      // Snapshot the previous value
      const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])

      // Optimistically update to the new value
      queryClient.setQueryData<Todo[]>(['todos'], (old = []) =>
        old.map(todo =>
          todo.id === id ? { ...todo, ...updates } : todo
        )
      )

      // Return a context object with the snapshotted value
      return { previousTodos }
    },
    onError: (error, variables, context) => {
      // If the mutation fails, use the context returned from onMutate
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos)
      }
    },
    onSettled: () => {
      // Always refetch after error or success
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  const deleteTodoMutation = useMutation({
    mutationFn: deleteTodo,
    onMutate: async (id) => {
      await queryClient.cancelQueries({ queryKey: ['todos'] })
      const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])

      queryClient.setQueryData<Todo[]>(['todos'], (old = []) =>
        old.filter(todo => todo.id !== id)
      )

      return { previousTodos }
    },
    onError: (error, id, context) => {
      if (context?.previousTodos) {
        queryClient.setQueryData(['todos'], context.previousTodos)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  const handleToggleTodo = (todo: Todo) => {
    updateTodoMutation.mutate({
      id: todo.id,
      updates: { completed: !todo.completed },
    })
  }

  const handleDeleteTodo = (id: number) => {
    deleteTodoMutation.mutate(id)
  }

  if (isLoading) return <div>Loading...</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Optimistic Todo List</h2>
      <p className="text-gray-600 mb-4">Changes appear instantly and rollback on errors</p>

      <ul className="space-y-2">
        {todos.slice(0, 10).map((todo) => (
          <li
            key={todo.id}
            className={`flex items-center gap-2 p-3 border rounded ${
              updateTodoMutation.isPending && updateTodoMutation.variables?.id === todo.id
                ? 'opacity-50'
                : ''
            }`}
          >
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => handleToggleTodo(todo)}
              disabled={updateTodoMutation.isPending}
              className="w-4 h-4"
            />
            <span className={todo.completed ? 'line-through text-gray-500' : ''}>
              {todo.title}
            </span>
            <button
              onClick={() => handleDeleteTodo(todo.id)}
              disabled={deleteTodoMutation.isPending}
              className="ml-auto px-2 py-1 text-sm bg-red-500 text-white rounded"
            >
              {deleteTodoMutation.isPending ? 'Deleting...' : 'Delete'}
            </button>
          </li>
        ))}
      </ul>
    </div>
  )
}

// 2. Infinite Scroll with useInfiniteQuery
interface Post {
  id: number
  title: string
  body: string
  userId: number
}

const fetchPostsPage = async ({ pageParam = 1 }): Promise<{
  data: Post[]
  page: number
  nextPage: number | null
}> => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/posts?_page=${pageParam}&_limit=10`
  )

  if (!response.ok) throw new Error('Failed to fetch posts')

  const posts = await response.json()
  return {
    data: posts,
    page: pageParam,
    nextPage: posts.length === 10 ? pageParam + 1 : null,
  }
}

function InfinitePostList() {
  const {
    data,
    isLoading,
    isError,
    error,
    hasNextPage,
    isFetchingNextPage,
    fetchNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts-infinite'],
    queryFn: fetchPostsPage,
    getNextPageParam: (lastPage) => lastPage.nextPage,
    initialPageParam: 1,
  })

  const posts = data?.pages.flatMap(page => page.data) ?? []

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Infinite Scroll Posts</h2>

      {isLoading && <div>Loading initial posts...</div>}
      {isError && <div className="text-red-500">Error: {error instanceof Error ? error.message : 'Unknown error'}</div>}

      <div className="space-y-4">
        {posts.map((post) => (
          <div key={post.id} className="p-4 border rounded">
            <h3 className="font-semibold text-lg">{post.title}</h3>
            <p className="text-gray-600 mt-2">{post.body}</p>
          </div>
        ))}
      </div>

      <div className="mt-6 text-center">
        <button
          onClick={() => fetchNextPage()}
          disabled={!hasNextPage || isFetchingNextPage}
          className="px-6 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {isFetchingNextPage
            ? 'Loading more...'
            : hasNextPage
            ? 'Load More'
            : 'No more posts'
          }
        </button>
      </div>
    </div>
  )
}

// 3. Multiple Queries with useQueries
function UserDashboard() {
  const [userId] = useState(1)

  const results = useQueries({
    queries: [
      {
        queryKey: ['user', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
          if (!response.ok) throw new Error('Failed to fetch user')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
      {
        queryKey: ['posts', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`)
          if (!response.ok) throw new Error('Failed to fetch posts')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
      {
        queryKey: ['todos', userId],
        queryFn: async () => {
          const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/todos`)
          if (!response.ok) throw new Error('Failed to fetch todos')
          return response.json()
        },
        staleTime: 1000 * 60 * 5,
      },
    ],
  })

  const [userResult, postsResult, todosResult] = results
  const isLoading = results.some(result => result.isLoading)
  const isError = results.some(result => result.isError)

  if (isLoading) return <div className="p-6">Loading dashboard...</div>
  if (isError) return <div className="p-6 text-red-500">Error loading dashboard data</div>

  const user = userResult.data
  const posts = postsResult.data ?? []
  const todos = todosResult.data ?? []

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">User Dashboard</h2>

      {user && (
        <div className="mb-6">
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">@{user.username}</p>
          <p className="text-gray-800">{user.email}</p>
        </div>
      )}

      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        <div>
          <h4 className="font-semibold mb-3">Recent Posts ({posts.length})</h4>
          <div className="space-y-2">
            {posts.slice(0, 3).map((post: any) => (
              <div key={post.id} className="p-3 bg-gray-50 rounded">
                <h5 className="font-medium">{post.title}</h5>
                <p className="text-sm text-gray-600 mt-1">{post.body.slice(0, 80)}...</p>
              </div>
            ))}
          </div>
        </div>

        <div>
          <h4 className="font-semibold mb-3">Todo Status</h4>
          <div className="space-y-2">
            <div className="flex justify-between p-3 bg-green-50 rounded">
              <span>Completed</span>
              <span className="font-medium">
                {todos.filter((todo: any) => todo.completed).length}
              </span>
            </div>
            <div className="flex justify-between p-3 bg-yellow-50 rounded">
              <span>Pending</span>
              <span className="font-medium">
                {todos.filter((todo: any) => !todo.completed).length}
              </span>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

// 4. Custom Hook Pattern
// Create a custom hook for user-related queries
function useUser(userId: number) {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
      if (!response.ok) throw new Error('Failed to fetch user')
      return response.json()
    },
    enabled: !!userId,
    staleTime: 1000 * 60 * 10, // 10 minutes
  })
}

function useUserPosts(userId: number) {
  return useQuery({
    queryKey: ['user-posts', userId],
    queryFn: async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}/posts`)
      if (!response.ok) throw new Error('Failed to fetch user posts')
      return response.json()
    },
    enabled: !!userId,
    staleTime: 1000 * 60 * 5, // 5 minutes
  })
}

function useCreatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: async (newPost: { title: string; body: string; userId: number }) => {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newPost),
      })
      if (!response.ok) throw new Error('Failed to create post')
      return response.json()
    },
    onSuccess: (newPost, variables) => {
      // Invalidate user posts query to refetch
      queryClient.invalidateQueries({
        queryKey: ['user-posts', variables.userId]
      })
    },
  })
}

// Component using custom hooks
function UserProfileWithPosts() {
  const [userId] = useState(1)
  const [showCreateForm, setShowCreateForm] = useState(false)

  const { data: user, isLoading: userLoading, error: userError } = useUser(userId)
  const { data: posts, isLoading: postsLoading, error: postsError } = useUserPosts(userId)
  const createPostMutation = useCreatePost()

  if (userLoading || postsLoading) return <div>Loading...</div>
  if (userError || postsError) return <div>Error loading data</div>

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">User Profile with Posts</h2>

      {user && (
        <div className="mb-6">
          <h3 className="text-xl font-semibold">{user.name}</h3>
          <p className="text-gray-600">{user.email}</p>
        </div>
      )}

      <div className="mb-4">
        <button
          onClick={() => setShowCreateForm(!showCreateForm)}
          className="px-4 py-2 bg-blue-500 text-white rounded"
        >
          {showCreateForm ? 'Hide Form' : 'Create New Post'}
        </button>
      </div>

      {showCreateForm && (
        <form
          onSubmit={(e) => {
            e.preventDefault()
            const formData = new FormData(e.currentTarget)
            createPostMutation.mutate({
              title: formData.get('title') as string,
              body: formData.get('body') as string,
              userId,
            })
          }}
          className="mb-6 p-4 border rounded bg-gray-50"
        >
          <div className="mb-3">
            <label className="block text-sm font-medium mb-1">Title</label>
            <input
              name="title"
              type="text"
              required
              className="w-full px-3 py-2 border rounded"
              disabled={createPostMutation.isPending}
            />
          </div>
          <div className="mb-3">
            <label className="block text-sm font-medium mb-1">Body</label>
            <textarea
              name="body"
              required
              rows={3}
              className="w-full px-3 py-2 border rounded"
              disabled={createPostMutation.isPending}
            />
          </div>
          <button
            type="submit"
            disabled={createPostMutation.isPending}
            className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
          >
            {createPostMutation.isPending ? 'Creating...' : 'Create Post'}
          </button>
        </form>
      )}

      <div>
        <h4 className="font-semibold mb-3">Posts ({posts?.length || 0})</h4>
        <div className="space-y-3">
          {posts?.map((post: any) => (
            <div key={post.id} className="p-3 border rounded">
              <h5 className="font-medium">{post.title}</h5>
              <p className="text-gray-600 text-sm mt-1">{post.body}</p>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

// 5. Background Refetching and Window Focus
function BackgroundRefetchExample() {
  const [enabled, setEnabled] = useState(true)
  const [refetchInterval, setRefetchInterval] = useState(5000) // 5 seconds

  const {
    data: todos = [],
    isLoading,
    error,
  } = useQuery({
    queryKey: ['todos', 'background'],
    queryFn: fetchTodos,
    refetchInterval: enabled ? refetchInterval : false,
    refetchIntervalInBackground: true,
    refetchOnWindowFocus: enabled,
    staleTime: 1000 * 30, // 30 seconds
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Background Refetching</h2>

      <div className="mb-4 space-y-2">
        <label className="flex items-center gap-2">
          <input
            type="checkbox"
            checked={enabled}
            onChange={(e) => setEnabled(e.target.checked)}
          />
          <span>Enable background refetching</span>
        </label>

        <div>
          <label className="block text-sm font-medium mb-1">
            Refetch Interval (ms): {refetchInterval}
          </label>
          <input
            type="range"
            min="1000"
            max="30000"
            step="1000"
            value={refetchInterval}
            onChange={(e) => setRefetchInterval(Number(e.target.value))}
            className="w-full"
          />
        </div>
      </div>

      {isLoading && <div>Loading...</div>}
      {error && <div className="text-red-500">Error loading data</div>}

      <div>
        <p className="text-sm text-gray-600 mb-2">
          Last updated: {new Date().toLocaleTimeString()}
        </p>
        <div className="text-sm text-gray-600 mb-4">
          Total todos: {todos.length}
        </div>

        <div className="space-y-2">
          {todos.slice(0, 5).map((todo: any) => (
            <div key={todo.id} className="flex items-center gap-2 p-2 border rounded">
              <input
                type="checkbox"
                checked={todo.completed}
                readOnly
                className="w-4 h-4"
              />
              <span className={todo.completed ? 'line-through text-gray-500' : ''}>
                {todo.title}
              </span>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

// 6. Query Observer for Manual Subscriptions
function useRealTimeData(queryKey: string[], queryFn: () => Promise<any>) {
  const [data, setData] = useState<any>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  useEffect(() => {
    const queryClient = new QueryClient()

    const observer = new QueryObserver(queryClient, {
      queryKey,
      queryFn,
      refetchInterval: 5000, // Refetch every 5 seconds
    })

    const subscription = observer.subscribe((result) => {
      setLoading(result.isLoading)
      setError(result.error as Error | null)
      setData(result.data)
    })

    return () => {
      subscription.unsubscribe()
    }
  }, [queryKey.toString()])

  return { data, loading, error }
}

function RealTimeDataComponent() {
  const { data, loading, error } = useRealTimeData(
    ['realtime-todos'],
    fetchTodos
  )

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Real-time Data</h2>
      <p className="text-gray-600 mb-4">Updates every 5 seconds</p>

      {loading && <div>Loading...</div>}
      {error && <div className="text-red-500">Error: {error.message}</div>}

      {data && (
        <div>
          <div className="text-sm text-gray-600 mb-2">
            Total todos: {data.length}
          </div>
          <div className="text-sm text-green-600 mb-4">
            Last update: {new Date().toLocaleTimeString()}
          </div>
        </div>
      )}
    </div>
  )
}

export {
  OptimisticTodoList,
  InfinitePostList,
  UserDashboard,
  UserProfileWithPosts,
  BackgroundRefetchExample,
  RealTimeDataComponent,
}
CódigotypescriptComplexo

Gerenciamento de Dados do TanStack Query

Padrões completos de gerenciamento de dados incluindo estratégias de cache, sincronização de dados, limites de erro e otimização de desempenho

Dificuldade

9/10

Tempo estimado

60 min

Etiquetas

tanstack-query, data-management, performance, optimization

Pré-requisitos

Advanced TanStack Query, React patterns, TypeScript, State management

typescript
// TanStack Query Advanced Data Management
// Comprehensive data management patterns and optimizations

import {
  useQuery,
  useMutation,
  useQueryClient,
  QueryClient,
  QueryClientProvider,
  useQueries,
  QueryObserver,
  QueryCache,
  MutationCache,
} from '@tanstack/react-query'
import { useState, useEffect, useCallback, useMemo, createContext, useContext } from 'react'

// 1. Advanced Query Client Configuration
const createAdvancedQueryClient = () => {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60 * 5, // 5 minutes
        cacheTime: 1000 * 60 * 10, // 10 minutes
        retry: (failureCount, error) => {
          // Don't retry on 4xx errors
          if (error && typeof error === 'object' && 'status' in error) {
            const status = (error as any).status
            if (status >= 400 && status < 500) {
              return false
            }
          }
          return failureCount < 3
        },
        retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
        refetchOnWindowFocus: false,
        refetchOnReconnect: true,
        notifyOnChangeProps: ['data', 'error', 'isLoading'],
      },
      mutations: {
        retry: 1,
        onMutate: () => {
          // Show loading indicator globally
        },
        onSuccess: () => {
          // Show success notification
        },
        onError: () => {
          // Show error notification
        },
      },
    },
    queryCache: new QueryCache({
      onError: (error, query) => {
        // Global query error handler
        console.error('Query error:', error, 'Query:', query.queryKey)
      },
      onSuccess: (data, query) => {
        // Global query success handler
        console.log('Query success:', query.queryKey)
      },
    }),
    mutationCache: new MutationCache({
      onError: (error, variables, context, mutation) => {
        // Global mutation error handler
        console.error('Mutation error:', error)
      },
    }),
  })
}

// 2. Data Context for Global State Management
interface DataContextType {
  invalidateQueries: (queryKey: string[]) => void
  setQueryData: (queryKey: string[], data: any) => void
  getQueryData: (queryKey: string[]) => any
  prefetchQuery: (queryKey: string[], queryFn: () => Promise<any>) => void
}

const DataContext = createContext<DataDataContextType | null>(null)

export function DataProvider({ children }: { children: React.ReactNode }) {
  const queryClient = useQueryClient()

  const invalidateQueries = useCallback((queryKey: string[]) => {
    queryClient.invalidateQueries({ queryKey })
  }, [queryClient])

  const setQueryData = useCallback((queryKey: string[], data: any) => {
    queryClient.setQueryData(queryKey, data)
  }, [queryClient])

  const getQueryData = useCallback((queryKey: string[]) => {
    return queryClient.getQueryData(queryKey)
  }, [queryClient])

  const prefetchQuery = useCallback((queryKey: string[], queryFn: () => Promise<any>) => {
    queryClient.prefetchQuery({ queryKey, queryFn })
  }, [queryClient])

  const value = useMemo(() => ({
    invalidateQueries,
    setQueryData,
    getQueryData,
    prefetchQuery,
  }), [invalidateQueries, setQueryData, getQueryData, prefetchQuery])

  return (
    <DataContext.Provider value={value}>
      {children}
    </DataContext.Provider>
  )
}

export function useDataContext() {
  const context = useContext(DataContext)
  if (!context) {
    throw new Error('useDataContext must be used within a DataProvider')
  }
  return context
}

// 3. Smart Data Fetching Hook with Caching
interface UseSmartFetchOptions {
  enabled?: boolean
  staleTime?: number
  cacheTime?: number
  refetchOnWindowFocus?: boolean
  retry?: boolean | number
}

function useSmartFetch<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: UseSmartFetchOptions = {}
) {
  const {
    enabled = true,
    staleTime = 1000 * 60 * 5,
    cacheTime = 1000 * 60 * 10,
    refetchOnWindowFocus = false,
    retry = 3,
  } = options

  return useQuery({
    queryKey,
    queryFn,
    enabled,
    staleTime,
    cacheTime,
    refetchOnWindowFocus,
    retry,
    select: useCallback((data: T) => {
      // Transform data if needed
      return data
    }, []),
  })
}

// 4. Offline-aware Data Management
function useOfflineAwareQuery<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: any = {}
) {
  const [isOnline, setIsOnline] = useState(navigator.onLine)

  useEffect(() => {
    const handleOnline = () => setIsOnline(true)
    const handleOffline = () => setIsOnline(false)

    window.addEventListener('online', handleOnline)
    window.addEventListener('offline', handleOffline)

    return () => {
      window.removeEventListener('online', handleOnline)
      window.removeEventListener('offline', handleOffline)
    }
  }, [])

  return useQuery({
    queryKey,
    queryFn,
    enabled: isOnline && (options.enabled ?? true),
    retry: (failureCount, error) => {
      // Retry more aggressively when coming back online
      if (isOnline && failureCount < 5) {
        return true
      }
      return false
    },
    refetchOnWindowFocus: isOnline,
    ...options,
  })
}

// 5. Data Synchronization Patterns
class DataSyncManager {
  private queryClient: QueryClient
  private syncQueue: Array<() => Promise<void>> = []
  private isSyncing = false

  constructor(queryClient: QueryClient) {
    this.queryClient = queryClient
    this.setupOnlineListener()
  }

  private setupOnlineListener() {
    window.addEventListener('online', () => {
      this.processSyncQueue()
    })
  }

  addToSyncQueue(syncFunction: () => Promise<void>) {
    this.syncQueue.push(syncFunction)
    if (navigator.onLine && !this.isSyncing) {
      this.processSyncQueue()
    }
  }

  private async processSyncQueue() {
    if (this.isSyncing || this.syncQueue.length === 0) return

    this.isSyncing = true
    console.log('Starting sync process...')

    while (this.syncQueue.length > 0) {
      const syncFunction = this.syncQueue.shift()
      try {
        await syncFunction()
      } catch (error) {
        console.error('Sync operation failed:', error)
        // Re-queue failed operations
        if (syncFunction) {
          this.syncQueue.push(syncFunction)
        }
      }
    }

    this.isSyncing = false
    console.log('Sync process completed')
  }

  async forceSync() {
    await this.processSyncQueue()
  }
}

// 6. Cache Management Utilities
function useCacheManager() {
  const queryClient = useQueryClient()

  const clearCache = useCallback(() => {
    queryClient.clear()
  }, [queryClient])

  const clearSpecificCache = useCallback((queryKey: string[]) => {
    queryClient.removeQueries({ queryKey })
  }, [queryClient])

  const getCacheSize = useCallback(() => {
    return queryClient.getQueryCache().getAll().length
  }, [queryClient])

  const prefetchMultipleQueries = useCallback((queries: Array<{
    queryKey: string[]
    queryFn: () => Promise<any>
  }>) => {
    queries.forEach(({ queryKey, queryFn }) => {
      queryClient.prefetchQuery({ queryKey, queryFn })
    })
  }, [queryClient])

  return {
    clearCache,
    clearSpecificCache,
    getCacheSize,
    prefetchMultipleQueries,
  }
}

// 7. Advanced Error Handling with Retry Logic
function useResilientQuery<T>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  options: {
    maxRetries?: number
    retryDelay?: number
    fallbackData?: T
    exponentialBackoff?: boolean
  } = {}
) {
  const {
    maxRetries = 3,
    retryDelay = 1000,
    fallbackData,
    exponentialBackoff = true,
  } = options

  return useQuery({
    queryKey,
    queryFn,
    retry: (failureCount, error: any) => {
      if (failureCount >= maxRetries) return false

      // Don't retry certain error types
      if (error?.status === 401 || error?.status === 403) {
        return false
      }

      return true
    },
    retryDelay: (attemptIndex) => {
      if (exponentialBackoff) {
        return Math.min(1000 * 2 ** attemptIndex, 30000)
      }
      return retryDelay
    },
    onError: (error) => {
      console.error('Query failed:', error)
    },
    fallbackData,
  })
}

// 8. Data Validation and Type Safety
function useValidatedQuery<T, R>(
  queryKey: string[],
  queryFn: () => Promise<T>,
  validator: (data: unknown) => R,
  options: any = {}
) {
  return useQuery({
    queryKey,
    queryFn: async () => {
      try {
        const data = await queryFn()
        return validator(data)
      } catch (error) {
        throw new Error(`Data validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
      }
    },
    ...options,
  })
}

// 9. Memory-efficient Data Management
function usePaginatedData<T>(
  baseQueryKey: string[],
  fetchPage: (page: number) => Promise<{ data: T[]; totalPages: number }>,
  options: {
    pageSize?: number
    cachePages?: number
  } = {}
) {
  const [currentPage, setCurrentPage] = useState(1)
  const { pageSize = 20, cachePages = 3 } = options

  const queryClient = useQueryClient()

  const {
    data,
    isLoading,
    error,
  } = useQuery({
    queryKey: [...baseQueryKey, 'page', currentPage],
    queryFn: () => fetchPage(currentPage),
    keepPreviousData: true,
    staleTime: 1000 * 60 * 5,
  })

  // Prefetch next and previous pages
  useEffect(() => {
    if (data && currentPage < data.totalPages) {
      queryClient.prefetchQuery({
        queryKey: [...baseQueryKey, 'page', currentPage + 1],
        queryFn: () => fetchPage(currentPage + 1),
      })
    }

    if (currentPage > 1) {
      queryClient.prefetchQuery({
        queryKey: [...baseQueryKey, 'page', currentPage - 1],
        queryFn: () => fetchPage(currentPage - 1),
      })
    }
  }, [currentPage, data, baseQueryKey, queryClient])

  return {
    data: data?.data ?? [],
    totalPages: data?.totalPages ?? 0,
    currentPage,
    setCurrentPage,
    isLoading,
    error,
  }
}

// 10. Real-time Data Integration
function useRealtimeSubscription<T>(
  queryKey: string[],
  initialData: T,
  subscribeToUpdates: (callback: (data: T) => void) => () => void
) {
  const queryClient = useQueryClient()

  const { data, isLoading, error } = useQuery({
    queryKey,
    queryFn: () => Promise.resolve(initialData),
    staleTime: Infinity, // Never stale, updated via subscription
  })

  useEffect(() => {
    const unsubscribe = subscribeToUpdates((newData) => {
      queryClient.setQueryData(queryKey, newData)
    })

    return unsubscribe
  }, [queryKey, queryClient, subscribeToUpdates])

  return { data, isLoading, error }
}

// 11. Comprehensive Example: E-commerce Product Management
interface Product {
  id: string
  name: string
  price: number
  description: string
  category: string
  stock: number
  images: string[]
  reviews: Review[]
}

interface Review {
  id: string
  productId: string
  userId: string
  rating: number
  comment: string
  createdAt: string
}

function ProductManagementApp() {
  const queryClient = useQueryClient()
  const [selectedCategory, setSelectedCategory] = useState<string>('all')
  const [searchTerm, setSearchTerm] = useState('')
  const [sortBy, setSortBy] = useState<'name' | 'price' | 'rating'>('name')

  // Fetch products with filters
  const {
    data: products = [],
    isLoading: productsLoading,
    error: productsError,
  } = useQuery({
    queryKey: ['products', { category: selectedCategory, searchTerm, sortBy }],
    queryFn: async () => {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 500))

      let filteredProducts = [
        // Mock data
        { id: '1', name: 'Laptop', price: 999, description: 'High-performance laptop', category: 'electronics', stock: 10, images: [], reviews: [] },
        { id: '2', name: 'Phone', price: 699, description: 'Smartphone', category: 'electronics', stock: 25, images: [], reviews: [] },
        { id: '3', name: 'Book', price: 19, description: 'Good book', category: 'books', stock: 100, images: [], reviews: [] },
      ]

      if (selectedCategory !== 'all') {
        filteredProducts = filteredProducts.filter(p => p.category === selectedCategory)
      }

      if (searchTerm) {
        filteredProducts = filteredProducts.filter(p =>
          p.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
          p.description.toLowerCase().includes(searchTerm.toLowerCase())
        )
      }

      return filteredProducts.sort((a, b) => {
        switch (sortBy) {
          case 'price':
            return a.price - b.price
          case 'name':
            return a.name.localeCompare(b.name)
          default:
            return 0
        }
      })
    },
    staleTime: 1000 * 60 * 5,
  })

  // Create product mutation with optimistic update
  const createProductMutation = useMutation({
    mutationFn: async (newProduct: Omit<Product, 'id' | 'reviews'>) => {
      const response = await fetch('/api/products', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newProduct),
      })
      if (!response.ok) throw new Error('Failed to create product')
      return response.json()
    },
    onMutate: async (newProduct) => {
      await queryClient.cancelQueries({ queryKey: ['products'] })

      const previousProducts = queryClient.getQueryData<Product[]>(['products'])

      const optimisticProduct: Product = {
        ...newProduct,
        id: 'temp-' + Date.now(),
        reviews: [],
      }

      queryClient.setQueryData<Product[]>(['products'], (old = []) => [
        optimisticProduct,
        ...old,
      ])

      return { previousProducts }
    },
    onError: (error, variables, context) => {
      if (context?.previousProducts) {
        queryClient.setQueryData(['products'], context.previousProducts)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] })
    },
  })

  // Update product mutation
  const updateProductMutation = useMutation({
    mutationFn: async ({ id, updates }: { id: string; updates: Partial<Product> }) => {
      const response = await fetch(`/api/products/${id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(updates),
      })
      if (!response.ok) throw new Error('Failed to update product')
      return response.json()
    },
    onMutate: async ({ id, updates }) => {
      await queryClient.cancelQueries({ queryKey: ['products'] })

      const previousProducts = queryClient.getQueryData<Product[]>(['products'])

      queryClient.setQueryData<Product[]>(['products'], (old = []) =>
        old.map(product =>
          product.id === id ? { ...product, ...updates } : product
        )
      )

      return { previousProducts }
    },
    onError: (error, variables, context) => {
      if (context?.previousProducts) {
        queryClient.setQueryData(['products'], context.previousProducts)
      }
    },
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] })
    },
  })

  return (
    <div className="p-6 border rounded-lg">
      <h2 className="text-2xl font-bold mb-4">Product Management</h2>

      {/* Filters and Search */}
      <div className="mb-6 grid grid-cols-1 md:grid-cols-4 gap-4">
        <input
          type="text"
          placeholder="Search products..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
          className="px-3 py-2 border rounded"
        />

        <select
          value={selectedCategory}
          onChange={(e) => setSelectedCategory(e.target.value)}
          className="px-3 py-2 border rounded"
        >
          <option value="all">All Categories</option>
          <option value="electronics">Electronics</option>
          <option value="books">Books</option>
        </select>

        <select
          value={sortBy}
          onChange={(e) => setSortBy(e.target.value as any)}
          className="px-3 py-2 border rounded"
        >
          <option value="name">Sort by Name</option>
          <option value="price">Sort by Price</option>
          <option value="rating">Sort by Rating</option>
        </select>

        <button
          onClick={() => createProductMutation.mutate({
            name: 'New Product',
            price: 99,
            description: 'New product description',
            category: 'electronics',
            stock: 50,
            images: [],
          })}
          disabled={createProductMutation.isPending}
          className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
        >
          Add Product
        </button>
      </div>

      {/* Products List */}
      {productsLoading ? (
        <div>Loading products...</div>
      ) : productsError ? (
        <div className="text-red-500">Error loading products</div>
      ) : (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {products.map((product) => (
            <div key={product.id} className="border rounded p-4">
              <h3 className="font-semibold">{product.name}</h3>
              <p className="text-gray-600">{product.description}</p>
              <p className="text-lg font-bold">${product.price}</p>
              <p className="text-sm text-gray-500">Stock: {product.stock}</p>
              <p className="text-sm text-gray-500">Category: {product.category}</p>

              <div className="mt-2 space-x-2">
                <button
                  onClick={() => updateProductMutation.mutate({
                    id: product.id,
                    updates: { price: product.price + 10 }
                  })}
                  disabled={updateProductMutation.isPending}
                  className="px-2 py-1 text-sm bg-blue-500 text-white rounded"
                >
                  Update Price
                </button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

export {
  createAdvancedQueryClient,
  DataProvider,
  useDataContext,
  useSmartFetch,
  useOfflineAwareQuery,
  DataSyncManager,
  useCacheManager,
  useResilientQuery,
  useValidatedQuery,
  usePaginatedData,
  useRealtimeSubscription,
  ProductManagementApp,
}

Ferramentas

Ferramentas frequentemente usadas com este exemplo

Relacionado

Coleções de exemplos relacionadas

Guia de uso da ferramenta

Contexto para entender esta coleção de exemplos

Título

Exemplos de TanStack Query

Descrição

Exemplos de TanStack Query (anteriormente React Query) incluindo busca de dados, cache, mutações, atualizações otimistas e padrões avançados

Visão geral

Exemplos de TanStack Query (anteriormente React Query) incluindo busca de dados, cache, mutações, atualizações otimistas e padrões avançados

Quando usar

  • Use this sample set when you want reference inputs for Exemplos de TanStack Query.
  • Use it to preview output formats and compare related sample variants.

Como funciona

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Casos de uso

  • Uso Básico do TanStack Query
  • Padrões Avançados do TanStack Query
  • Gerenciamento de Dados do TanStack Query

Perguntas frequentes

What is included in Exemplos de TanStack Query?

This page includes curated sample entries, related tools, and neighboring sample collections for Exemplos de TanStack Query.

Início
Ferramentas3197
Exemplos593
Visualizações461
Visualizadores de arquivos103
Temas229
Etiquetas333
Imagens, áudio e vídeo655

Ferramentas de processamento, conversão e otimização de imagens, áudio e vídeo

Matemática, datas e finanças644

Calculadoras, ferramentas numéricas, lógica de datas, estatística e finanças

Design e cor296

Ferramentas de cor, layout, gráficos, estilo visual e apoio ao design

Texto e escrita209

Ferramentas de escrita, limpeza, formatação, extração e análise de texto

Desenvolvimento e Web193

Utilitários para desenvolvedores, rede, depuração web e automação

Conversão e codificação175

Ferramentas de conversão de formatos, arquivos, unidades, arquivos compactados e codificação

Estruturar uma captura de interface, revisar e entregar ao desenvolvimento
Esboço de piano, revisão MIDI e entrega de uma demonstração de áudio
Revisão de texturas retrô e entrega de recursos
Revisão e entrega de guias de procedimento com capturas
Revisão visual de alterações responsivas e entrega de evidências

© 2026 Elysia Tools. Todos os direitos reservados.

DocumentaçãoPreçosSobreTermos de serviçoPolítica de privacidadeCookiesContato

Development

Construtor de Consultas SQL

Construir consultas SQL simples visualmente

Network

Ferramenta de Consulta DNS

Consulte registros DNS e informações de resolução de domínio incluindo A, AAAA, MX, TXT, NS e CNAME

Science & Education

Scientific Constants Query

Search and display scientific constants with customizable language and output format

Development

Analisador de Parâmetros URL

Analise em lote os parâmetros de consulta de URL com saída de frequência de chaves, exemplos de valores e detecção de anomalias de codificação

Data Processing

Studio de consulta e transformacao JSONata

Visualize consultas e transformacoes no estilo JSONata, compare resultados e exporte em JSON, CSV, YAML ou Markdown.

Development

Ferramenta de consulta JSONPath

Consulta qualquer JSON com expressoes JSONPath, visualiza correspondencias, troca o formato de saida e salva modelos reutilizaveis

Development

Formatador e minificador de SQL

Embeleza ou minifica SQL com controle total do dialeto (PostgreSQL, MySQL, SQLite, SQL Server/T-SQL, BigQuery, Snowflake etc.), caixa de palavras-chave/identificadores/funções, estilo de recuo (padrão, tabular esquerda/direita) e linhas entre instruções. Produz um resultado realçado e pronto para copiar com estatísticas de tamanho e instruções.