use-st8
use-st8 copied to clipboard
Persist the setter reference within re-renders (same as use-state setter)
function useSt8<V>(initialValue: (() => V) | V): St8<V> {
const state = useRef(initialValue);
const reRender = useReducer((s) => s + 1, 0)[1];
return useMemo(() => {
if (typeof state.current === 'function') {
state.current = state.current();
}
return function st8() {
if (!arguments.length) return state.current;
const [value] = arguments;
state.current =
typeof value === 'function' ? value(state.current) : value;
reRender();
};
}, []) as St8<V>;
}
Looking react doc as a reference, here are some things which could be interpreted as spec for set-state. (I know this function must not be an exact replacement for useState
, but making it more similar with no cost, make it considerable)
- [x]
initialState
: The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. - [x] ↪️ This argument is ignored after the initial render.
- [x] ↪️ React will call your initializer function when initializing the component, and store its return value as the initial state.
useState returns an array with exactly two values:
-
[x] The current state. During the first render, it will match the initialState you have passed.
-
[x] ↪️ The set function that lets you update the state to a different value and trigger a re-render.
-
[ ] ⚠️ In Strict Mode, React will call your initializer function twice in order to help you find accidental impurities. This is development-only behavior and does not affect production. If your initializer function is pure (as it should be), this should not affect the behavior. The result from one of the calls will be ignored.
-
[x] set functions do not have a return value.
-
[ ] ❌ If the new value you provide is identical to the current state, as determined by an Object.is comparison, React will skip re-rendering the component and its children. This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn’t affect your code.
This need additional check for calling the re-render (it is easy, but seems extra to me!)[⭐️]
-
[ ] ⚠️ React batches state updates. It updates the screen after all the event handlers have run and have called their set functions. This prevents multiple re-renders during a single event.
-
[ ] ⚠️ Updating objects and arrays in state
Updating object and arrays, is not longer need immutability, although work with immutability too (see [⭐️]), same as
useImmer
:) obj.x = 10; // ❇️ Can also mutate existing object setObj(obj); // ❇️ update the sate
- [x] You can reset a component’s state by passing a different key to a component.
Passing key will unmount component, so the ref will be reseted too :)
- [x] The identity of the setCount function is guaranteed to be stable so it’s safe to omit (ref)
function useSt8<V>(initialValue: (() => V) | V): St8<V> {
const state = useRef(initialValue);
const reRender = useReducer((s) => s + 1, 0)[1];
return useMemo(() => {
if (typeof state.current === "function") {
state.current = state.current();
}
return function st8() {
if (!arguments.length) return state.current;
const [value] = arguments;
let next = typeof value === "function" ? value(state.current) : value;
if (!Object.is(next, current.state)) {
state.current = next;
reRender();
}
};
}, []) as St8<V>;
}