0.2.2 • Published 2 years ago

@yortyrh/react-on-value-change v0.2.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

React useValueChange · npm.io Netlify Status Contributor Covenant

Subscribe to value changes and call the onChange function. This make sure calls to the onChange method are done in changes order. The onChange method can be a promise, no additional on change method called if the promise is pending

Install

npm install --save @yortyrh/react-on-value-change

//or

yarn add @yortyrh/react-on-value-change

Example

Demo: https://react-on-value-change.netlify.app

import * as React from 'react';
import { useValueChange } from '@yortyrh/react-on-value-change';
import './index.scss';

function abortError(message = 'Aborted') {
  return new DOMException(message, 'AbortError');
}

function waitMillis(millis: number, abortSignal?: AbortSignal): Promise<void> {
  return new Promise<void>((res, rej) => {
    let aborted = false;
    const timeout = setTimeout(() => (aborted ? undefined : res()), millis);

    if (abortSignal) {
      abortSignal.onabort = () => {
        aborted = true;
        clearTimeout(timeout);
        rej(abortError());
      };
    }
  });
}

async function delayHello(
  who: string,
  abortSignal?: AbortSignal
): Promise<string> {
  await waitMillis(500, abortSignal);
  return `Hello ${who}`;
}

interface Movie {
  Title: string;
  Year: string;
  imdbID: string;
  Type: 'movie' | 'series' | 'episode';
  Poster: string;
}

async function searchMovies(q: string, signal?: AbortSignal): Promise<Movie[]> {
  const response = await fetch(
    `https://www.omdbapi.com/?apikey=63ebddab&s=${q}`,
    { signal }
  );
  const data: { Search?: Movie[] } = await response.json();
  return data.Search || [];
}

const App = () => {
  const [text, setText] = React.useState('');
  const [q, setQ] = React.useState('');
  const [movies, setMovies] = React.useState<Movie[]>([]);
  const [greeting, setGreeting] = React.useState('');

  useValueChange(
    text,
    async (value, _previous, abortSignal) => {
      setGreeting(await delayHello(value, abortSignal));
    },
    {
      frequency: 1,
      abortPromise: true,
    }
  );

  useValueChange(
    q,
    async (value, _previous, abortSignal) => {
      setMovies(await searchMovies(value, abortSignal));
    },
    {
      frequency: 1,
      abortPromise: true,
    }
  );

  return (
    <section>
      <h1>React useValueChange examples</h1>
      <article>
        <h2 className="SectionTitle">Hello Who</h2>
        <input
          type="text"
          className="SearchInput"
          onInput={(ev) => setText((ev.target as HTMLInputElement).value)}
        />
        <div className="Greeting">{greeting}</div>
      </article>

      <article>
        <h2 className="SectionTitle">Search Movie</h2>
        <input
          type="text"
          className="SearchInput"
          onInput={(ev) => setQ((ev.target as HTMLInputElement).value)}
        />
        <ul className="Movies">
          {movies.map((movie) => (
            <li key={movie.imdbID} className="Movies__Item">
              <a
                href={`https://www.imdb.com/title/${movie.imdbID}`}
                target="_blank"
                rel="noopener"
                className="Movies__Item__ImgLink"
              >
                <img
                  src={movie.Poster}
                  alt={movie.Title}
                  className="Movies__Item__Img"
                />
              </a>
              <h4>{movie.Title}</h4>
              <p>
                <strong>Type:</strong> {movie.Type}
              </p>
              <p>
                <strong>Year:</strong> {movie.Year}
              </p>
            </li>
          ))}
        </ul>
      </article>
    </section>
  );
};