🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis →
Socket
Book a DemoInstallSign in
Socket

@ribbon-studios/react-utils

Package Overview
Dependencies
Maintainers
2
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ribbon-studios/react-utils

Collection of react utilities curated by the Ribbon Studios Team~

latest
Source
npmnpm
Version
4.0.1
Version published
Maintainers
2
Created
Source

NPM Version NPM Downloads Coveralls

CI Build Maintainability Semantic Release Code Style: Prettier

React Utils đź”§

Collection of react utilities curated by Ribbon Studios Team~

Hooks

useCachedState

import { useCachedState } from '@ribbon-studios/react-utils';

export type MySimpleInputProps = {
  value?: string;
};

export function MySimpleInput({ value: externalValue }: MySimpleInputProps) {
  // This is a utility for keeping external properties in-sync with the internal state
  const [value, setValue] = useCachedState(() => externalValue, [externalValue]);

  return <input value={value} onChange={(event) => setValue(event.target.value)} />;
}

useSubtleCrypto

import { useSubtleCrypto } from '@ribbon-studios/react-utils';

export type ProfileProps = {
  email?: string;
};

export function Profile({ email }: ProfileProps) {
  const hashedEmail = useSubtleCrypto('SHA-256', email);

  return <img src={`https://gravatar.com/avatar/${hashedEmail}.jpg`} />;
}

React Router

useLoaderData

import { useLoaderData } from '@ribbon-studios/react-utils/react-router';

export async function loader() {
  return {
    hello: 'world',
  };
}

export function Profile() {
  // No more type casting!
  const value = useLoaderData<typeof loader>();

  return value.hello;
}

<Await/>

import { useLoaderData, Await } from '@ribbon-studios/react-utils/react-router';

export async function loader() {
  return Promise.resolve({
    greetings: Promise.resolve(['hello world', 'hallo welt']),
  });
}

export function Profile() {
  const data = useLoaderData<typeof loader>();

  return (
    <Await resolve={data.greetings}>
      /* Retains the type! */
      {(greetings) => (
        <>
          {greetings.map((greeting, i) => (
            <div key={i}>{greeting}</div>
          ))}
        </>
      )}
    </Await>
  );
}

Testing Utilities

hooked

import { useMemo } from 'react';
import { hooked } from '@ribbon-studios/react-utils';

const useMyHook = (value: string) => useMemo(() => value, [value]);

const HookedComponent = hooked(useMyHook);

it('should ...', async () => {
  // Properties are forwarded to your component as you'd expect
  render(<HookedComponent hook="Hello world!" />);

  expect(screen.getByText('Hello world!')).toBeTruthy();
});

Multiple Arguments

import { useMemo } from 'react';
import { hooked } from '@ribbon-studios/react-utils';

const useMyHook = (value: string, otherValue: string) => useMemo(() => `${value} ${otherValue}`, [value, otherValue]);

const HookedComponent = hooked(useMyHook);

it('should ...', async () => {
  // Properties are forwarded to your component as you'd expect
  render(<HookedComponent hook={['Hello world!', 'Hallo welt!']} />);

  expect(screen.getByText('Hello world! Hallo welt!')).toBeTruthy();
});

Type Map

TypeOutputExample
stringstring'hello world'
numbernumber.toString()'1'
booleanboolean.toString()'true'
undefined'<undefined>''<undefined>'
objectJSON.stringify(object)'{ "hello": "world" }'
arrayJSON.stringify(array)'["hello", "world"]'

wrap

This utility is more for testing purposes to easily create wrappers for other components.

import { wrap } from '@ribbon-studios/react-utils';
import { MemoryRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const Router = wrap(MemoryRouter);
const ReactQuery = wrap(QueryClientProvider, () => ({
  client: new QueryClient(),
}));

it('should ...', async () => {
  const Component = await Router(ReactQuery(import('../MyComponent.tsx')));

  // Properties are forwarded to your component as you'd expect
  render(<Component value="Hello world!" />);

  // ...
});

wrap.concat

Helper function for wrappers that combines them together, useful if you need the whole kitchen sink!

import { wrap } from '@ribbon-studios/react-utils';
import { MemoryRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const Router = wrap(MemoryRouter);
const ReactQuery = wrap(QueryClientProvider, () => ({
  client: new QueryClient(),
}));

const KitchenSink = wrap.concat(Router, ReactQuery);

it('should ...', async () => {
  const Component = await KitchenSink(import('../MyComponent.tsx')));

  // Properties are forwarded to your component as you'd expect
  render(<Component value="Hello world!" />);

  // ...
});

Built-Ins

We have a variety of wrappers for libraries built-in to simplify testing!

import { HelmetProvider } from '@ribbon-studios/react-utils/react-helmet';
import { QueryClientProvider } from '@ribbon-studios/react-utils/react-query';
import { MemoryRouter } from '@ribbon-studios/react-utils/react-router';

const KitchenSink = wrap.concat(HelmetProvider, QueryClientProvider, MemoryRouter);

it('should ...', async () => {
  const Component = await KitchenSink(import('../MyComponent.tsx')));

  render(<Component value="Hello world!" />);

  // ...
});

Want to Contribute?

FAQs

Package last updated on 05 Sep 2025

Did you know?

Socket

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.

Install

Related posts