
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
@foundrykit/hooks
Advanced tools
A collection of custom React hooks for common UI patterns and functionality. Provides reusable logic for state management, user interactions, and component behavior.
A collection of custom React hooks for common UI patterns and functionality. Provides reusable logic for state management, user interactions, and component behavior.
pnpm add @foundrykit/hooks
Persist state in localStorage with automatic serialization:
import { useLocalStorage } from '@foundrykit/hooks';
function UserPreferences() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
const [language, setLanguage] = useLocalStorage('language', 'en');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value='light'>Light</option>
<option value='dark'>Dark</option>
</select>
<select value={language} onChange={e => setLanguage(e.target.value)}>
<option value='en'>English</option>
<option value='es'>Spanish</option>
</select>
</div>
);
}
Debounce values to improve performance:
import { useDebouncedValue } from '@foundrykit/hooks';
function SearchComponent() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebouncedValue(query, 300);
// This effect only runs when debouncedQuery changes
useEffect(() => {
if (debouncedQuery) {
searchAPI(debouncedQuery);
}
}, [debouncedQuery]);
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder='Search...'
/>
);
}
React to media query changes:
import { useMediaQuery } from '@foundrykit/hooks';
function ResponsiveComponent() {
const isMobile = useMediaQuery('(max-width: 768px)');
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
const isDesktop = useMediaQuery('(min-width: 1025px)');
return (
<div>
{isMobile && <MobileLayout />}
{isTablet && <TabletLayout />}
{isDesktop && <DesktopLayout />}
</div>
);
}
Detect clicks outside an element:
import { useClickOutside } from '@foundrykit/hooks';
function Dropdown({ isOpen, onClose, children }) {
const ref = useRef(null);
useClickOutside(ref, () => {
if (isOpen) {
onClose();
}
});
if (!isOpen) return null;
return (
<div ref={ref} className='dropdown'>
{children}
</div>
);
}
Manage dialog/modal state:
import { useDialog } from '@foundrykit/hooks';
function ModalExample() {
const { isOpen, open, close, toggle } = useDialog();
return (
<div>
<button onClick={open}>Open Modal</button>
{isOpen && (
<div className='modal-overlay' onClick={close}>
<div className='modal-content' onClick={e => e.stopPropagation()}>
<h2>Modal Content</h2>
<button onClick={close}>Close</button>
</div>
</div>
)}
</div>
);
}
Complete form state management:
import { useForm } from '@foundrykit/hooks';
function ContactForm() {
const {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
} = useForm({
initialValues: {
name: '',
email: '',
message: '',
},
validate: values => {
const errors = {};
if (!values.name) errors.name = 'Name is required';
if (!values.email) errors.email = 'Email is required';
if (!values.message) errors.message = 'Message is required';
return errors;
},
onSubmit: async values => {
await submitForm(values);
},
});
return (
<form onSubmit={handleSubmit}>
<input
name='name'
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
className={touched.name && errors.name ? 'error' : ''}
/>
{touched.name && errors.name && <span>{errors.name}</span>}
<input
name='email'
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
className={touched.email && errors.email ? 'error' : ''}
/>
{touched.email && errors.email && <span>{errors.email}</span>}
<textarea
name='message'
value={values.message}
onChange={handleChange}
onBlur={handleBlur}
className={touched.message && errors.message ? 'error' : ''}
/>
{touched.message && errors.message && <span>{errors.message}</span>}
<button type='submit' disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
</form>
);
}
Access the previous value:
import { usePrevious } from '@foundrykit/hooks';
function Counter() {
const [count, setCount] = useState(0);
const previousCount = usePrevious(count);
return (
<div>
<p>Current: {count}</p>
<p>Previous: {previousCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
Set up intervals with automatic cleanup:
import { useInterval } from '@foundrykit/hooks';
function Timer() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useInterval(() => setSeconds(seconds + 1), isRunning ? 1000 : null);
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={() => setIsRunning(!isRunning)}>
{isRunning ? 'Pause' : 'Start'}
</button>
</div>
);
}
Combine multiple hooks for complex functionality:
import {
useLocalStorage,
useDebouncedValue,
useMediaQuery,
} from '@foundrykit/hooks';
function useResponsiveSearch() {
const [query, setQuery] = useLocalStorage('search-query', '');
const debouncedQuery = useDebouncedValue(query, 300);
const isMobile = useMediaQuery('(max-width: 768px)');
const searchResults = useMemo(() => {
if (!debouncedQuery) return [];
return performSearch(debouncedQuery, { limit: isMobile ? 5 : 10 });
}, [debouncedQuery, isMobile]);
return {
query,
setQuery,
debouncedQuery,
searchResults,
isMobile,
};
}
import { useClickOutside } from '@foundrykit/hooks';
function useDropdown(initialState = false) {
const [isOpen, setIsOpen] = useState(initialState);
const ref = useRef(null);
useClickOutside(ref, () => setIsOpen(false));
const open = useCallback(() => setIsOpen(true), []);
const close = useCallback(() => setIsOpen(false), []);
const toggle = useCallback(() => setIsOpen(!isOpen), [isOpen]);
return {
isOpen,
ref,
open,
close,
toggle,
};
}
import { useDebouncedValue } from '@foundrykit/hooks';
function OptimizedSearch() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebouncedValue(query, 500);
// Memoize expensive operation
const searchResults = useMemo(() => {
if (!debouncedQuery) return [];
return expensiveSearch(debouncedQuery);
}, [debouncedQuery]);
return (
<div>
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder='Search...'
/>
<SearchResults results={searchResults} />
</div>
);
}
All hooks include comprehensive TypeScript definitions:
import { useLocalStorage, useDebouncedValue } from '@foundrykit/hooks';
// Type-safe localStorage hook
const [user, setUser] = useLocalStorage<User>('user', null);
// Type-safe debounced value
const [searchTerm, setSearchTerm] = useState<string>('');
const debouncedSearchTerm = useDebouncedValue<string>(searchTerm, 300);
// ✅ Good - Stable callback
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
// ❌ Avoid - Unstable callback
const handleClick = () => {
console.log('clicked');
};
// ✅ Good - Proper cleanup
useEffect(() => {
const timer = setTimeout(() => {
// do something
}, 1000);
return () => clearTimeout(timer);
}, []);
// ❌ Avoid - Missing cleanup
useEffect(() => {
setTimeout(() => {
// do something
}, 1000);
}, []);
// ✅ Good - Memoized expensive operations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
// ❌ Avoid - Recomputing on every render
const expensiveValue = computeExpensiveValue(data);
When adding new hooks:
MIT
FAQs
A collection of custom React hooks for common UI patterns and functionality. Provides reusable logic for state management, user interactions, and component behavior.
The npm package @foundrykit/hooks receives a total of 1,025 weekly downloads. As such, @foundrykit/hooks popularity was classified as popular.
We found that @foundrykit/hooks demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.