Skip to content

Commit

Permalink
[feat]: useMin and useMax renewed and organized
Browse files Browse the repository at this point in the history
  • Loading branch information
changeelog committed Jun 6, 2024
1 parent 6237291 commit 67ad9fe
Show file tree
Hide file tree
Showing 16 changed files with 288 additions and 28 deletions.
Empty file added hooks/Math/useMax/CHANGELOG.md
Empty file.
Empty file added hooks/Math/useMax/README.md
Empty file.
67 changes: 67 additions & 0 deletions hooks/Math/useMax/__tests__/useMax.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { renderHook } from '@testing-library/react';
import { useMax } from '../useMax';

describe('useMax', () => {
it('should return the maximum value from an array', () => {
const { result } = renderHook(() => useMax([1, 2, 3, 4]));
expect(result.current).toBe(4);
});

it('should return the maximum value from multiple arguments', () => {
const { result } = renderHook(() => useMax(1, 3, 2));
expect(result.current).toBe(3);
});

it('should handle an empty array', () => {
const { result } = renderHook(() => useMax([]));
expect(result.current).toBe(-Infinity);
});

it('should handle no arguments', () => {
const { result } = renderHook(() => useMax());
expect(result.current).toBe(-Infinity);
});

it('should handle a single argument', () => {
const { result } = renderHook(() => useMax(5));
expect(result.current).toBe(5);
});

it('should handle a mix of direct values and getter functions', () => {
const value1 = 10;
const value2 = () => 20;
const value3 = () => 15;

const { result } = renderHook(() => useMax(value1, value2, value3));
expect(result.current).toBe(20);
});

it('should handle an array of direct values and getter functions', () => {
const value1 = 10;
const value2 = () => 20;
const value3 = () => 15;

const { result } = renderHook(() => useMax([value1, value2, value3]));
expect(result.current).toBe(20);
});

it('should memoize the result and only recompute when dependencies change', () => {
const value1 = 10;
const value2 = () => 20;

const { result, rerender } = renderHook(
({ value1, value2 }) => useMax(value1, value2),
{
initialProps: { value1, value2 },
}
);

expect(result.current).toBe(20);

rerender({ value1, value2 });
expect(result.current).toBe(20);

rerender({ value1: 30, value2 });
expect(result.current).toBe(30);
});
});
1 change: 1 addition & 0 deletions hooks/Math/useMax/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useMax';
7 changes: 7 additions & 0 deletions hooks/Math/useMax/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"baseUrl": "."
},
"include": ["src", "index.ts"]
}
16 changes: 16 additions & 0 deletions hooks/Math/useMax/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from 'tsup';

export default defineConfig({
clean: true,
target: 'es2019',
format: ['cjs', 'esm'],
banner: { js: '"use client";' },
sourcemap: true,
minify: false,
outExtension({ format }) {
return {
js: `.${format}.js`,
ts: `.${format}.ts`,
};
},
});
42 changes: 42 additions & 0 deletions hooks/Math/useMax/useMax.demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { useMax } from './useMax';

const UseMaxDemo = () => {
const [num1, setNum1] = React.useState(5);
const [num2, setNum2] = React.useState(10);
const maxNum = useMax(num1, num2);

return (
<div className="flex flex-col items-center justify-center h-screen">
<h1 className="text-4xl font-bold mb-4">useMax Demo</h1>
<div className="flex items-center mb-4">
<label className="mr-2">Enter a number:</label>
<input
type="number"
value={num1}
onChange={e => setNum1(parseInt(e.target.value, 10))}
max={10}
min={-10}
className="mr-2"
/>
<label>Maximum value:</label>
<p className="text-2xl font-bold mb-4">{maxNum}</p>
</div>
<div className="flex items-center">
<label className="mr-2">Enter another number:</label>
<input
type="number"
value={num2}
onChange={e => setNum2(parseInt(e.target.value, 10))}
max={10}
min={-10}
className="mr-2"
/>
<label>Maximum value:</label>
<p className="text-2xl font-bold">{maxNum}</p>
</div>
</div>
);
};

export default UseMaxDemo;
43 changes: 29 additions & 14 deletions hooks/Math/useMax/useMax.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import { useCallback, useEffect, useState } from "react";
import { useMemo } from 'react';

type UpdateMaxFn = (newNumbers: number[]) => void;
type ValueOrFunction<T> = T | (() => T);

export const useMax = (numbers: number[]): [number, UpdateMaxFn] => {
const [max, setMax] = useState<number>(() => Math.max(...numbers));
function resolveValue<T>(value: ValueOrFunction<T>): T {
return typeof value === 'function' ? (value as Function)() : value;
}

const updateMax: UpdateMaxFn = useCallback((newNumbers: number[]) => {
setMax(Math.max(...newNumbers));
}, []);

useEffect(() => {
updateMax(numbers);
}, [numbers, updateMax]);

return [max, updateMax];
};
/**
* Returns the maximum value from the given arguments or array of values.
*
* @param {...any[]} args - The values or arrays of values to compare.
* @returns {number} The maximum value among the given arguments or array of values.
*
* @example
* const maxValue = useMax(1, 2, 3);
* // maxValue is 3
*
* @example
* const maxValue = useMax([1, 2, 3]);
* // maxValue is 3
*/
export function useMax(...args: any[]): number {
return useMemo(() => {
if (args.length === 1 && Array.isArray(resolveValue(args[0]))) {
const array = resolveValue(args[0]) as ValueOrFunction<number>[];
return Math.max(...array.map(resolveValue));
} else {
return Math.max(...args.map(resolveValue));
}
}, args);
}
Empty file added hooks/Math/useMin/CHANGELOG.md
Empty file.
Empty file added hooks/Math/useMin/README.md
Empty file.
32 changes: 32 additions & 0 deletions hooks/Math/useMin/__tests__/useMin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { renderHook } from '@testing-library/react';
import { useMin } from '../useMin';

describe('useMin', () => {
it('should return the minimum of a single array', () => {
const { result } = renderHook(() => useMin([1, 2, 3, 4, -1, 10]));
expect(result.current).toBe(-1);
});

it('should return the minimum of multiple arguments', () => {
const { result } = renderHook(() => useMin(5, 10, 15, -5, 0));
expect(result.current).toBe(-5);
});

it('should handle references or functions inside an array', () => {
const ref1 = 7;
const ref2 = () => 3;
const ref3 = 5;
const { result } = renderHook(() => useMin([ref1, ref2, ref3]));
expect(result.current).toBe(3);
});

it('should return Infinity for an empty array', () => {
const { result } = renderHook(() => useMin([]));
expect(result.current).toBe(Infinity);
});

it('should return Infinity for empty arguments', () => {
const { result } = renderHook(() => useMin());
expect(result.current).toBe(Infinity);
});
});
1 change: 1 addition & 0 deletions hooks/Math/useMin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useMin';
7 changes: 7 additions & 0 deletions hooks/Math/useMin/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"baseUrl": "."
},
"include": ["src", "index.ts"]
}
16 changes: 16 additions & 0 deletions hooks/Math/useMin/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from 'tsup';

export default defineConfig({
clean: true,
target: 'es2019',
format: ['cjs', 'esm'],
banner: { js: '"use client";' },
sourcemap: true,
minify: false,
outExtension({ format }) {
return {
js: `.${format}.js`,
ts: `.${format}.ts`,
};
},
});
42 changes: 42 additions & 0 deletions hooks/Math/useMin/useMin.demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { useMin } from './useMin';

const UseMinDemo = () => {
const [num1, setNum1] = React.useState(5);
const [num2, setNum2] = React.useState(10);
const minNum = useMin(num1, num2);

return (
<div className="flex flex-col items-center justify-center h-screen">
<h1 className="text-4xl font-bold mb-4">useMin Demo</h1>
<div className="flex items-center mb-4">
<label className="mr-2">Enter a number:</label>
<input
type="number"
value={num1}
onChange={e => setNum1(parseInt(e.target.value, 10))}
max={10}
min={-10}
className="mr-2"
/>
<label>Minimum value:</label>
<p className="text-2xl font-bold mb-4">{minNum}</p>
</div>
<div className="flex items-center">
<label className="mr-2">Enter another number:</label>
<input
type="number"
value={num2}
onChange={e => setNum2(parseInt(e.target.value, 10))}
max={10}
min={-10}
className="mr-2"
/>
<label>Minimum value:</label>
<p className="text-2xl font-bold">{minNum}</p>
</div>
</div>
);
};

export default UseMinDemo;
42 changes: 28 additions & 14 deletions hooks/Math/useMin/useMin.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
import { useEffect, useRef, useState } from 'react'
import { useMemo } from 'react';

type UpdateMinFn = (newNumbers: number[]) => void
type ValueOrFunction<T> = T | (() => T);

export const useMin = (numbers: number[]): [number, UpdateMinFn] => {
const [min, setMin] = useState<number>(() => Math.min(...numbers))
const updateMinRef = useRef<UpdateMinFn>(() => {})
function resolveValue<T>(value: ValueOrFunction<T>): T {
return typeof value === 'function' ? (value as Function)() : value;
}

useEffect(() => {
const updateMin: UpdateMinFn = (newNumbers: number[]) => {
setMin(Math.min(...newNumbers))
/**
* Returns the minimum value from the given arguments.
*
* The `useMin` hook accepts any number of arguments, which can be either
* individual values or arrays of values. If an array is provided, the hook
* will return the minimum value from the array. If individual values are
* provided, the hook will return the minimum value from those values.
*
* @param {...(number | (() => number) | number[])} args - The values or arrays of values from which to find the minimum.
* @returns {number} The minimum value from the given arguments.
*
* @example
* const minValue = useMin(1, 2, 3, 4, -1, 10); // Returns -1
* const minArrayValue = useMin([5, 10, 15, -5, 0]); // Returns -5
*/
export function useMin(...args: any[]): number {
return useMemo(() => {
if (args.length === 1 && Array.isArray(resolveValue(args[0]))) {
const array = resolveValue(args[0]) as ValueOrFunction<number>[];
return Math.min(...array.map(resolveValue));
} else {
return Math.min(...args.map(resolveValue));
}

updateMin(numbers)
updateMinRef.current = updateMin
}, [numbers])

return [min, updateMinRef.current]
}, args);
}

0 comments on commit 67ad9fe

Please sign in to comment.