Skip to content

Commit

Permalink
+ useProxyRef hook
Browse files Browse the repository at this point in the history
+ `useForwardedProxyRef` hook
  • Loading branch information
dzek69 committed May 13, 2024
1 parent de3c777 commit a64441d
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 104 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ All notable changes to this project will be documented in this file.
The format is based on [EZEZ Changelog](https://ezez.dev/guidelines/changelog)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [UNRELEASED]
## [2.2.0] - 2024-05-13
### Added
- WIP `useProxyRef` hook
- `useProxyRef` hook
- `useForwardedProxyRef` hook

## [2.1.1] - 2024-05-05
### Fixed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ If a hook is there, it's either unique, rare, super optimized or just needed for
- 🌐 `useSimpleGeolocation` - keep track of user's geolocation (simplified)
- ☝️ `useInputDevice` - keep track of a currently active input device
- 🥇 `useIsFirstRender` - keep track of first render
- 🎭 `useProxyRef` - wraps your ref with a Proxy for full control
- 🎭 `useForwardedProxyRef` - wraps forwarded ref with a Proxy for full control
- 📅 `useToday` - keep track of day changes
- 🔄 `useUpdateEvery` - force render every interval set

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ezez/hooks",
"version": "2.1.1+",
"version": "2.2.0",
"repository": "[email protected]:dzek69/ezez-hooks.git",
"author": "Jacek Nowacki",
"license": "MIT",
Expand Down Expand Up @@ -61,7 +61,7 @@
"husky": "^8.0.3",
"jest": "^29.6.1",
"must": "^0.13.4",
"next": "^14.0.4",
"next": "^14.2.3",
"nodemon": "^3.0.1",
"prettier": "^2.8.8",
"react": "^18.2.0",
Expand Down
101 changes: 50 additions & 51 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

95 changes: 95 additions & 0 deletions src/demos/useForwadedProxyRef/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { forwardRef, useRef, useState } from "react";

import { useForwardedProxyRef } from "../../useForwardedProxyRef";

interface Props {}

const noDucksProxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> = {
set(input, prop, value) {
console.info("setting value on ducks proxy", value, input.value, value === input.value);

if (value === "duck") {
console.error("No ducks allowed!");
return true;
}

if (prop === "value" && typeof value === "string") {
// eslint-disable-next-line no-param-reassign
input[prop] = value;
}
return true;
},
};
// @ts-expect-error Just a debug helper
noDucksProxy.name = "No ducks";

const noChickenProxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> = {
set(input, prop, value) {
console.info("setting value on chicken proxy", value, input.value, value === input.value);

if (value === "chicken") {
console.error("No chickens allowed!");
return true;
}

if (prop === "value" && typeof value === "string") {
// eslint-disable-next-line no-param-reassign
input[prop] = value;
}
return true;
},
};
// @ts-expect-error Just a debug helper
noChickenProxy.name = "No chicken";

const Input = forwardRef<
HTMLInputElement | null, { proxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> }
>((props, ref) => {
const theRef = useForwardedProxyRef(ref, props.proxy);

// const standardRef = useRef<Date | number | null>(5);
// const wrapped = useForwardedProxyRef(standardRef, {});
// console.info("Standard ref", { wrapped, standardRef });

return (
<input ref={theRef} />
);
});

// eslint-disable-next-line react/no-multi-comp
const UseForwardedProxyRef: React.FC<Props> = () => {
const [number, setNumber] = useState(0); // just to trigger re-renders

const [currentProxy, setCurrentProxy] = useState<
ProxyHandler<HTMLInputElement | HTMLTextAreaElement>
>(noDucksProxy);

const ref = useRef<HTMLInputElement | null>(null);

const setRefValue = (value: string) => {
if (!ref.current) {
console.error("No ref!");
return;
}
ref.current.value = value;
};

return (
<div>
Currently set proxy: {currentProxy === noDucksProxy ? "ducks preventing proxy" : "chicken preventing proxy"}
<button onClick={() => { setCurrentProxy(noDucksProxy); }}>set proxy to ducks</button>
<button onClick={() => { setCurrentProxy(noChickenProxy); }}>set proxy to chicken</button>
<hr />
Input ref #{number}: <hr />
<Input key={number} ref={ref} proxy={currentProxy} />
<button onClick={() => { setNumber(p => p + 1); }}>Change ref</button>
<hr />
Value manipulation:
<button onClick={() => { setRefValue("lorem"); }}>call ref.current.value = "lorem";</button>
<button onClick={() => { setRefValue("duck"); }}>call ref.current.value = "duck";</button>
<button onClick={() => { setRefValue("chicken"); }}>call ref.current.value = "chicken";</button>
</div>
);
};

export { UseForwardedProxyRef };
11 changes: 8 additions & 3 deletions src/demos/useProxyRef/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useRef, useState } from "react";
import React, { useState } from "react";

import { useProxyRef } from "../../useProxyRef";

Expand All @@ -20,6 +20,8 @@ const noDucksProxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> = {
return true;
},
};
// @ts-expect-error Just a debug helper
noDucksProxy.name = "No ducks";

const noChickenProxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> = {
set(input, prop, value) {
Expand All @@ -37,12 +39,15 @@ const noChickenProxy: ProxyHandler<HTMLInputElement | HTMLTextAreaElement> = {
return true;
},
};
// @ts-expect-error Just a debug helper
noChickenProxy.name = "No chicken";

const UseProxyRef: React.FC<Props> = () => {
const renderCount = useRef(0);
const [number, setNumber] = useState(0); // just to trigger re-renders

const [currentProxy, setCurrentProxy] = useState<ProxyHandler<HTMLInputElement | HTMLTextAreaElement>>(noDucksProxy);
const [currentProxy, setCurrentProxy] = useState<
ProxyHandler<HTMLInputElement | HTMLTextAreaElement>
>(noDucksProxy);

const ref = useProxyRef<HTMLInputElement | null>(null, currentProxy);

Expand Down
Loading

0 comments on commit a64441d

Please sign in to comment.