Skip to content

Instantly share code, notes, and snippets.

@mgtitimoli
Created January 21, 2021 02:48

Revisions

  1. mgtitimoli created this gist Jan 21, 2021.
    27 changes: 27 additions & 0 deletions Test.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    import React from "react";

    import type {ReactStateAccessor} from "./useStateAccessor";

    type State = {
    count: number;
    };

    type StateAccessor = ReactStateAccessor<State>

    const increaseCount = ({count}: State) => ({count: count + 1});

    const render = (stateAccesor: StateAccessor) => (
    <div>
    <div>Count: {stateAccessor()}</div>
    <button onClick={() => stateAccessor()}>Count: {stateAccessor(increaseCount)}</button>
    <div>
    )

    const initialState = {
    count: 0
    };

    const Test = () =>
    render(useStateAccessor(initialState));

    export default Test;
    34 changes: 34 additions & 0 deletions useStateAccessor.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    import {useRef, useState} from "react";

    import type {SetStateAction} from "react";

    type ReactStateAccessor<State> = (
    ...args: [] | [SetStateAction<State>]
    ) => State;

    const useStateAccessors = <State>(
    initialState: State
    ): ReactStateAccessor<State> => {
    const [, setState] = useState(initialState);

    const stateRef = useRef(initialState);

    return (...args) => {
    if (args.length === 1) {
    const [newStateOrSetter] = args;

    stateRef.current =
    newStateOrSetter instanceof Function
    ? newStateOrSetter(stateRef.current)
    : newStateOrSetter;

    setState(stateRef.current);
    }

    return stateRef.current;
    };
    };

    export default useStateAccessors;

    export type {ReactStateAccessor};