@ihaz/react-ui-utils
TypeScript icon, indicating that this package has built-in type declarations

1.4.1 • Public • Published

React UI Utils

A general UI common methods for general React Projects.

Quick Start

This library is made for every React project (React DOM and React Native) means the library has no more dependencies than React Core or any Third Party Dependencies made for single React Project.

Advantages

  • Library 100% React Core made only, so it can be used for all React Projects, such a React Dom and React Native.
  • Free of any dependencies.
  • Provides common Utilities that resolve common React Project uses.
  • Easy user interface implementations.

Documentation

This library provides the following utilities:

createViewManager

Import

import { createViewManager } from "@ihaz/react-ui-utils";

Create a view manager that can handle the mount-unmount behavior from the own parent Tree component through the show and onClose methods. Every component is added to internal Parent component state, components inherit a prop called onClose when the method is called it will remove from the internal state, the method can also return a result when the component is closed.

🔷 This HOC is useful if it wants to render views thats has or not any relation to the component which its invoked or it can be render outside the component, such a Modals or Dialogs.

Usage

import { createViewManager } from "@ihaz/react-ui-utils";

const manager = createViewManager();

const ViewComponent = ({ onClose }: ViewProps<string>) => {
  return (
    <div>
      <h1>View example</h1>
      <button onClick={() => onClose("hello")}>Close</button>
    </div>
  );
};

const Example = () => {
  const onShow = async () => {
    const response = await manager.show(ViewComponent);
    console.log(response); // Hello
  };

  return (
    <div>
      <button onClick={onShow}>Show</button>
      <manager.Component />
    </div>
  );
};

Methods

show

Mount the passing instance Component adding it to parent Tree collection. Every Child Component inherits a onClose method that unmount and remove of the parent Tree collection and return a result in his argument, when it´s called a promise is resolved and return the result setted.

Example

import { ViewManager, type ViewProps } from "@ihaz/react-ui-utils";

const manager = ViewManager.createViewManager();

function BasicDemo({ onClose }: ViewProps<boolean>) {
  return (
    <Dialog
      header="Header"
      visible
      style={{ width: "50vw" }}
      onHide={() => onClose(false)}
    >
      <Button
        label="True Response"
        icon="pi pi-external-link"
        onClick={() => onClose(true)}
      />
      <Button
        label="False Response"
        icon="pi pi-external-link"
        onClick={() => onClose(false)}
      />
      <p className="m-0">Lorem ipsum ...</p>
    </Dialog>
  );
}

function App() {
  const onShow = async () => {
    const response = await manager.show(BasicDemo); //Promise will be resolved when `onClose` method is called and return a result if is declared
    console.log(response);
  };

  return (
    <main>
      {/*Component must be included in any part of your React Project for declare its own instance*/}
      <manager.Component /> <button onClick={onShow}>Show Dialog</button>
    </main>
  );
}

export default App;

showSync Method with same effect as show method, the child component is mount in parent Tree collection and inherits a onClose method. this function return the following methods.

  • show: Mount the child component

  • close: Unmount the child component, if onClose method from child component is called previously, this one has no effect.

If any result is declared in onClose argument, it will be show in onCloseListenner callback.

Example

import { ViewManager, type ViewProps } from "@ihaz/react-ui-utils";

const manager = ViewManager.createViewManager();

function BasicDemo({ onClose }: ViewProps<boolean>) {
  return (
    <Dialog
      header="Header"
      visible
      style={{ width: "50vw" }}
      onHide={() => onClose(false)}
    >
      <Button
        label="True Response"
        icon="pi pi-external-link"
        onClick={() => onClose(true)}
      />
      <Button
        label="False Response"
        icon="pi pi-external-link"
        onClick={() => onClose(false)}
      />
      <p className="m-0">Lorem ipsum ...</p>
    </Dialog>
  );
}

function App() {
  const onCloseListenner = (value: boolean) => {
    console.log({ value }); // Return the value
  };

  const onShow = () => {
    const dialog = manager.showSync(BasicDemo, undefined, onCloseListenner); // onCloseListenner callback is called when onClose method is called inside component
    dialog.start(); // Mount Component
  };

  return (
    <main>
      {/*Component must be included in any part of your React Project for declare its own instance*/}
      <manager.Component />
      <button onClick={onShow}>Show Dialog</button>
    </main>
  );
}

export default App;

Merge Props You can merge props of the child component its depends the own component lifecycle.

Example

import { ViewManager, type ViewProps } from "@ihaz/react-ui-utils";

const manager = ViewManager.createViewManager();

interface BasicDemoProps extends ViewProps<boolean> {
  name: string;
}

function BasicDemo({ onClose, name }: BasicDemoProps) {
  return (
    <Dialog
      header="Header"
      visible
      style={{ width: "50vw" }}
      onHide={() => onClose(false)}
    >
      <Button
        label="True Response"
        icon="pi pi-external-link"
        onClick={() => onClose(true)}
      />
      <Button
        label="False Response"
        icon="pi pi-external-link"
        onClick={() => onClose(false)}
      />
      <p className="m-0">{name}</p>
    </Dialog>
  );
}

function App() {
  const onShow = async () => {
    const res = await manager.show(BasicDemo, { name: "Hello" });
    console.log({ res });
  };

  return (
    <main>
      <manager.Component />
      <button onClick={onShow}>Show Dialog</button>
    </main>
  );
}

export default App;

Context View Child components works as stand alone component that it can render without depending if the parent component is mount or not in main React Tree, so the solution to unmount the view child component if the parent view is unmount if set a context to create a View Environment.

Example

import { ViewManager, type ViewProps } from "@ihaz/react-ui-utils";

const manager = ViewManager.createViewManager();

const CONTEXT = "render_view"; // Context to this View Environment, it works like a unique  key

interface BasicDemoProps extends ViewProps<boolean> {
  name: string;
  setShowRender: Dispatch<SetStateAction<boolean>>;
}

function BasicDemo({ onClose, name, setShowRender }: BasicDemoProps) {
  return (
    <Dialog
      header="Header"
      visible
      style={{ width: "50vw" }}
      onHide={() => onClose(false)}
    >
      <Button
        label="True Response"
        icon="pi pi-external-link"
        onClick={() => onClose(true)}
      />
      <Button
        label="False Response"
        icon="pi pi-external-link"
        onClick={() => onClose(false)}
      />
      <Button
        label="UnMount Parent Component"
        icon="pi pi-external-link"
        onClick={() => setShowRender(false)}
      />
      <p className="m-0">{name}</p>
    </Dialog>
  );
}

interface RenderViewComponentProps {
  setShowRender: Dispatch<SetStateAction<boolean>>; //set a state dispatcher to unmount the parent component
}

function RenderViewComponent({ setShowRender }: RenderViewComponentProps) {
  const onShow = async () => {
    const res = await manager.show(
      BasicDemo,
      { name: "Hello", setShowRender },
      CONTEXT // set the same context than parent component to bind the same view environment
    );
    console.log({ res });
  };

  return <button onClick={onShow}>Show Dialog</button>;
}

const RenderView = manager.createViewContextComponent(
  RenderViewComponent,
  CONTEXT
); // HOC that register the parent component to internal Tree with a context key

function App() {
  const [showRender, setShowRender] = useState<boolean>(true);

  return (
    <main>
      <manager.Component />
      {showRender && <RenderView setShowRender={setShowRender} />} {/* When its unmount, view manager will remove all the child components with same context key, returning `undefined` value*/}
    </main>
  );
}

export default App;

createUncontrolledFC

createUncontrolledClassComponent

Import

import { createUncontrolledClassComponent } from "@ihaz/react-ui-utils";

Provides a React Class Component where the internal methods can be malipulated by other React component throught their own instance.

Example

import { Component } from "react";
import { createUncontrolledClassComponent } from "@ihaz/react-ui-utils";

class ExampleClassComponent extends Component<any, {selector: number}> {
  constructor(props: any) {
      super(props);
      this.state = {
          selector: 1
      }
  }

  getSelector = () => this.state.selector;
  changeSelector = (new_selector: number) => this.setState({ selector: new_selector });

  render() {
      return ... //JSX
  }
}


const UncontrolledExample = createUncontrolledClassComponent(ExampleClassComponent, {
  getSelector: (instance) => instance().getSelector(),
changeSelector: (instance, new_selector: number) => instance().changeSelector(new_selector),
});


export const Main = () => {

  const show = () => {
     console.log(UncontrolledExample.getSelector()) // 1
  }
    const changeSelector = () => {
  UncontrolledExample.changeSelector(2); //Changes internal selector state to 2
};

  return <>
<UncontrolledExample.Component />
  <button onClick={show}>Show Selector</button>
  <button onClick={changeSelector}>Change Selector</button>
  </>
}

withStatus

Is a HOC

createFormManager

Import

import { createFormManager } from "@ihaz/react-ui-utils";

Returns a Form enviroment that provides a Form component that can be used and handled by internal state using a Field component that can change every prop from the state

Example

import { createFormManager } from "@ihaz/react-ui-utils";

const initial = {
  name: "",
  lastName: "",
  age: 0,
  birthAge: new Date(),
};

const { Form, Field, Submit, useFormValue } = createFormManager(initial, {
  name: (value) => value.length > 10, // Validation for the name
});
const Component = () => {
  const { value, isValidated } = useFormValue();

  const onSubmit = (result: typeof initial) => {
    console.log(result); // Current state of form value
    //...
  };

  return (
    <Form onSubmit={onSubmit}>
      <Field field="name" />
      <Field field="lastName" />
      <Field field="birthAge" />
      <Field field="age" />
      <Submit>
        <button type="submit">submit</button>
      </Submit>
    </Form>
  );
};

Hooks

useEffectAsync

Import

import { useEffectAsync } from "@ihaz/react-ui-utils";

Effect with same function as React.useEffect that can be declared a promise in the callback.

Example

import { useEffectAsync } from "@ihaz/react-ui-utils";
import { useState } from "react";

const Example = () => {
  const [value, setValue] = useState();

  useEffectAsync(async () => {
    const api = await fetch("myapi");
    setValue(api);
  }, []);

  return <div>{value}</div>;
};

useLayoutEffectAsync

Import

import { useLayoutEffectAsync } from "@ihaz/react-ui-utils";

Effect with same function as React.useLayoutEffect that can be declared a promise in the callback.

Example

import { useLayoutEffectAsync } from "@ihaz/react-ui-utils";
import { useState } from "react";

const Example = () => {
  const [value, setValue] = useState();

  useLayoutEffectAsync(async () => {
    const api = await fetch("myapi");
    setValue(api);
  }, []);

  return <div>{value}</div>;
};

useIntervalEffect

Import

import { useIntervalEffect } from "@ihaz/react-ui-utils";

Hook that execute a callback into a Interval.

Example

import { useIntervalEffect } from "@ihaz/react-ui-utils";
import { useState } from "react";

const Example = () => {
  const [count, setCount] = useState(0);
  const { restart, stop } = useIntervalEffect(
    ({ count }) => {
      console.log(count); // Shows the current count value in every interval
      setCount((prev) => prev + 1);
    },
    1000,
    { count }
  ); //Executes effect every second

  return (
    <div>
      <button onClick={() => restart()}>Restart</button>
      <button onClick={() => stop()}>Stop</button>
      <span>{count}</span>
    </div>
  );
};

Arguments

  • effect: Callback that executes in every interval.
  • ms: Optional. Time interval in milliseconds to executes every callback.
  • intervalValues: Optional. An object of Values that use the current value in every interval.
  • inmediate: Optional. Execute the effect inmediatly, before the interval has initializated. If ms parameter is not declared this arg execute the effect twice at first.

Caveats

  • The stop method if executeCallbackCleanup is declared, executes the last interval cleanup.
  • The internal effect has not dependencies besides a state that refresh the interval, in case you want to use a React state value or any async value, you can put it on intervalValues argument:
const [count, setCount] = useState(0);

// Wrong ❌
useIntervalEffect(() => {
  console.log(count);
}, 1000);

// Good ✅
useIntervalEffect(
  ({ countDependency }) => {
    console.log(countDependency);
  },
  1000,
  { countDependency: count }
);

Note The interval effect dependency effect return the async previus value in every interval, so to a interval setted its get the current value that returns the dependency

useValueHandler

Import

import { useValueHandler } from "@ihaz/react-ui-utils";

Hook that provides an uncontrolled internal state storing the value with ref, meaning the value handler never affects the component lifecycle and always get the stored value synchronously.

Example

import { useValueHandler } from "@ihaz/react-ui-utils";

const Example = () => {
  const [counter, setCounter] = useValueHandler(0); // initial 0

  const handleChange = () => {
    setCounter((prev) => prev + 1); //increment
    console.log(counter()); //value incremented synchronously
  };

  return <button onChange={handleChange}>count: {counter()}</button>; //Never changes till you change a state that modify the lifecycle component
};

Caveats

  • useValueHandler is a hook that store a value by ref, so this hook never update the component.
  • For callbacks and effects that use the value stored always return the current value, in case if the callback value is called in jsx, it must have a trigger or state that update the component for show the current value.
  • The value (similar to React State conditions) returns a read-only and non-mutable value that can be only change by the dispatch method, so don´t change or mutate the value directly.

useEventHandler

Import

import { useEventHandler } from "@ihaz/react-ui-utils";

Hook that executes a callback suscribing to one or several events into a component.

Example

import { useEventHandler } from "@ihaz/react-ui-utils";

const Example = () => {
  const { addEventListenner, removeEventListenner, listen } = useEventHandler<
    "change",
    string
  >();

  useEffect(() => {
    const listenner = (result: string) => {
      console.log("result: ", result);
    };
    addEventListenner("change", listenner); // Suscribing callback to Change event

    return () => {
      removeEventListenner("change", listenner); // Unsuscribing callback to Change event
    };
  }, []);

  const handleFetch = async () => {
    const res = await fetch("myapi");
    listen("change", await res.text()); // Listen Change event
  };

  return <button onClick={handleFetch}>Fetch</button>;
};

Dependents (0)

Package Sidebar

Install

npm i @ihaz/react-ui-utils

Weekly Downloads

9

Version

1.4.1

License

MIT

Unpacked Size

405 kB

Total Files

10

Last publish

Collaborators

  • ilanarellano22