npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@devesharp/react-hooks-v2

v1.1.20

Published

Coleção de hooks React customizados para desenvolvimento eficiente

Downloads

359

Readme

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default tseslint.config({
  extends: [
    // Remove ...tseslint.configs.recommended and replace with this
    ...tseslint.configs.recommendedTypeChecked,
    // Alternatively, use this for stricter rules
    ...tseslint.configs.strictTypeChecked,
    // Optionally, add this for stylistic rules
    ...tseslint.configs.stylisticTypeChecked,
  ],
  languageOptions: {
    // other options...
    parserOptions: {
      project: ['./tsconfig.node.json', './tsconfig.app.json'],
      tsconfigRootDir: import.meta.dirname,
    },
  },
})

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default tseslint.config({
  plugins: {
    // Add the react-x and react-dom plugins
    'react-x': reactX,
    'react-dom': reactDom,
  },
  rules: {
    // other rules...
    // Enable its recommended typescript rules
    ...reactX.configs['recommended-typescript'].rules,
    ...reactDom.configs.recommended.rules,
  },
})

My Hooks Lib

Uma biblioteca de hooks customizados para React, desenvolvida em TypeScript.

📦 Instalação

npm install my-hooks-lib
# ou
yarn add my-hooks-lib
# ou
pnpm add my-hooks-lib

🚀 Uso

import { useDebounce, useLocalStorage, useToggle } from 'my-hooks-lib'

🧠 Hooks Disponíveis

useDebounce

Hook para debounce de valores, útil para otimizar pesquisas e inputs.

import { useDebounce } from 'my-hooks-lib'

function SearchComponent() {
  const [searchTerm, setSearchTerm] = useState('')
  const debouncedSearchTerm = useDebounce(searchTerm, 500)

  useEffect(() => {
    if (debouncedSearchTerm) {
      // Fazer busca apenas após 500ms de inatividade
      performSearch(debouncedSearchTerm)
    }
  }, [debouncedSearchTerm])

  return (
    <input
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
      placeholder="Digite para buscar..."
    />
  )
}

useIsMounted

Hook para verificar se o componente ainda está montado, útil para evitar vazamentos de memória.

import { useIsMounted } from 'my-hooks-lib'

function AsyncComponent() {
  const isMounted = useIsMounted()
  const [data, setData] = useState(null)

  useEffect(() => {
    fetchData().then(result => {
      if (isMounted()) {
        setData(result)
      }
    })
  }, [isMounted])

  return <div>{data}</div>
}

useLocalStorage

Hook para gerenciar dados no localStorage com sincronização entre abas.

import { useLocalStorage } from 'my-hooks-lib'

function SettingsComponent() {
  const [theme, setTheme] = useLocalStorage('theme', 'light')
  const [user, setUser] = useLocalStorage('user', { name: '', email: '' })

  return (
    <div>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Tema atual: {theme}
      </button>
      <input
        value={user.name}
        onChange={(e) => setUser(prev => ({ ...prev, name: e.target.value }))}
        placeholder="Nome"
      />
    </div>
  )
}

useToggle

Hook para gerenciar estados booleanos com funções de toggle.

import { useToggle } from 'my-hooks-lib'

function ModalComponent() {
  const [isOpen, toggle, setIsOpen] = useToggle(false)

  return (
    <div>
      <button onClick={toggle}>
        {isOpen ? 'Fechar' : 'Abrir'} Modal
      </button>
      <button onClick={() => setIsOpen(true)}>
        Forçar Abrir
      </button>
      {isOpen && <div>Conteúdo do Modal</div>}
    </div>
  )
}

useFetch

Hook para fazer requisições HTTP com estados de loading e error.

import { useFetch } from 'my-hooks-lib'

function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error } = useFetch(`/api/users/${userId}`)

  if (loading) return <div>Carregando...</div>
  if (error) return <div>Erro: {error}</div>
  if (!data) return <div>Usuário não encontrado</div>

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.email}</p>
    </div>
  )
}

useViewList - Gerenciamento Avançado de Listas

import { useViewList } from '@devesharp/react-hooks-v2';

function UserList() {
  const {
    resources: users,
    resourcesTotal,
    isSearching,
    isFirstPage,
    isLastPage,
    setFilters,
    nextPage,
    previousPage,
    setSort,
  } = useViewList<User, UserFilters>({
    resolveResources: async (filters) => {
      const response = await fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(filters),
      });
      return response.json();
    },
    limit: 10,
    // Novos callbacks para eventos
    onBeforeSearch: (filters) => {
      console.log('Iniciando busca:', filters);
    },
    onAfterSearch: (result) => {
      if (result.success) {
        console.log(`Carregados ${result.data?.results.length} usuários`);
      } else {
        console.error('Erro na busca:', result.error);
      }
    },
    onChangeFilters: (newFilters, previousFilters) => {
      // Sincronizar com URL
      const params = new URLSearchParams();
      if (newFilters.search) params.set('search', newFilters.search);
      window.history.replaceState({}, '', `?${params.toString()}`);
    },
  });

  return (
    <div>
      {/* Busca */}
      <input
        type="text"
        placeholder="Buscar usuários..."
        onChange={(e) => setFilters({ search: e.target.value })}
      />
      
      {/* Ordenação */}
      <select onChange={(e) => {
        const [column, direction] = e.target.value.split(':');
        setSort(column ? { column, direction: direction as 'asc' | 'desc' } : null);
      }}>
        <option value="">Sem ordenação</option>
        <option value="name:asc">Nome (A-Z)</option>
        <option value="name:desc">Nome (Z-A)</option>
      </select>

      {/* Lista */}
      {isSearching && <div>Carregando...</div>}
      {users.map(user => (
        <div key={user.id}>
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </div>
      ))}

      {/* Paginação */}
      <button onClick={previousPage} disabled={isFirstPage}>
        Anterior
      </button>
      <span>Total: {resourcesTotal}</span>
      <button onClick={nextPage} disabled={isLastPage}>
        Próxima
      </button>
    </div>
  );
}

Principais Funcionalidades

Navegação:

  • nextPage(): Avança para a próxima página
  • previousPage(): Volta para a página anterior
  • setPage(pageNumber): Navega para uma página específica (começando em 0)
  • retry(): Tenta novamente a última requisição que falhou

Filtros:

  • setFilters(newFilters): Atualiza filtros e reinicia a busca
  • filters: Estado atual dos filtros (inclui offset para paginação)

Ordenação:

  • setSort(sort): Atualiza ordenação mantendo a página atual
  • initialSort: Define ordenação inicial (padrão: null)
  • Aceita null ou { column: string | null, direction: 'asc' | 'desc' }

Manipulação de Recursos:

  • pushResource(resource): Adiciona um novo recurso à lista
  • updateResource(id, resource): Substitui completamente um recurso
  • putResource(id, partialResource): Atualiza parcialmente um recurso
  • deleteResource(id): Remove um recurso da lista
  • deleteManyResources(ids): Remove múltiplos recursos
  • changePosition(id, newPosition): Altera a posição de um recurso

Estados:

  • isSearching: Indica se uma busca está em andamento
  • isErrorOnSearching: Indica se houve erro na busca
  • isFirstPage: Indica se está na primeira página
  • isLastPage: Indica se está na última página

🛠️ Desenvolvimento

Scripts Disponíveis

# Build da biblioteca
npm run build

# Linting
npm run lint

# Desenvolvimento (se usando com Vite)
npm run dev

Estrutura do Projeto

my-hooks-lib/
├── src/
│   ├── hooks/
│   │   ├── useDebounce.ts
│   │   ├── useIsMounted.ts
│   │   ├── useLocalStorage.ts
│   │   ├── useToggle.ts
│   │   ├── useFetch.ts
│   │   └── index.ts
│   └── index.ts
├── dist/                 # Arquivos compilados
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── README.md

📋 Requisitos

  • React >= 17
  • TypeScript (recomendado)

🤝 Contribuindo

  1. Fork o projeto
  2. Crie uma branch para sua feature (git checkout -b feature/AmazingFeature)
  3. Commit suas mudanças (git commit -m 'Add some AmazingFeature')
  4. Push para a branch (git push origin feature/AmazingFeature)
  5. Abra um Pull Request

📄 Licença

Este projeto está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.

🔗 Links Úteis

Testes do useDebounce

Este arquivo contém testes abrangentes para o hook useDebounce.

📋 Cenários Testados

✅ Comportamento Básico

  • Valor inicial: Verifica se o hook retorna o valor inicial imediatamente
  • Delay padrão: Testa se o debounce funciona com o delay padrão de 500ms
  • Delay customizado: Verifica se delays personalizados funcionam corretamente

✅ Comportamento Avançado

  • Cancelamento de timeout: Testa se timeouts anteriores são cancelados quando o valor muda rapidamente
  • Mudança de delay: Verifica se o hook responde corretamente quando o delay é alterado
  • Delay zero: Testa o comportamento com delay zero

✅ Tipos de Dados

  • String: Valores de texto
  • Number: Valores numéricos
  • Object: Objetos complexos
  • Array: Arrays de dados

✅ Gerenciamento de Memória

  • Cleanup: Verifica se timeouts são limpos quando o componente é desmontado
  • Referência: Testa se a referência do valor é mantida quando não há mudanças

🧪 Executando os Testes

# Executar todos os testes
npm run test

# Executar testes uma vez
npm run test:run

# Executar testes com coverage
npm run test:coverage

🔧 Ferramentas Utilizadas

  • Vitest: Framework de testes
  • @testing-library/react: Utilitários para testar hooks React
  • jsdom: Ambiente DOM para testes
  • Fake Timers: Para controlar setTimeout/clearTimeout nos testes

📊 Cobertura de Testes

Os testes cobrem:

  • ✅ Todas as linhas de código do hook
  • ✅ Todos os cenários de uso comum
  • ✅ Casos extremos (edge cases)
  • ✅ Gerenciamento de memória
  • ✅ Diferentes tipos de dados