@ihaz/react-ui-utils
v1.4.1
Published
A cross-platform general UI common methods for React Projects
Downloads
26
Maintainers
Readme
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 commonReact
Project uses. - Easy user interface implementations.
Documentation
This library provides the following utilities:
- HOCs
- Hooks
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.
:large_blue_diamond: 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 ifexecuteCallbackCleanup
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>;
};