Skip to content

Instantly share code, notes, and snippets.

@nyteshade
Created December 11, 2023 19:08
Show Gist options
  • Save nyteshade/aa3d265ec2327f60d09616113a159a1d to your computer and use it in GitHub Desktop.
Save nyteshade/aa3d265ec2327f60d09616113a159a1d to your computer and use it in GitHub Desktop.
Experimentation with a non-react specific useState approximation
/**
* The function `usePseudoState` is a custom hook that allows for the creation of
* pseudo state variables in React. When `useState` becomes available, it should be
* applied to this object output by calling the `init` method with the `useState`
* function.
*
* The object returned from this function invocation
*
* @param initialValue - The `initialValue` parameter is the initial value that
* will be assigned to the pseudo state.
* @returns The function `usePseudoState` returns an object with several properties
* and methods.
*/
export
function usePseudoState(
initialValue,
{ reactUseStateFn, mockState } = { reactUseStateFn: undefined, mockState: false }
) {
const mockedSymbol = Symbol.for('usePseudoState::mocked');
const pseudoState = {
// Tries to fetch the first value from the reactState current value, if the
// react state is not initialized then it will return the initialValue
get() { return this.reactState ? this.reactState?.[0] : this.initialValue },
// Tries to set the first value of the reactState current value, if the react
// state is not initialized then it will do nothing
set(value) { this.reactState?.[1](value) },
// The initial value of the pseudo state, also applied to react state if
// init() is called on this object
initialValue,
// The react useState hook values. If reactUseStateFn is provided, it will
// be initialized immediately. Otherwise it allows for lazy initialization
// using the init method.
reactState: undefined,
// Provided a useState function, from within a React component, the internal
// reactState will be initialized and the pseudoState will be ready for use
init(useStateFn) {
const value = this.reactState?.[0] ?? this.initialValue
this.reactState = useStateFn(value)
},
// Returns true if the reactState is initialized, false otherwise
get valid() { return this.reactState !== undefined },
// Returns true if the reactState is valid, but is also mocked
get mocked() { return this.valid && this.reactState[mockedSymbol] === true },
// The toStringTag property is used by the Object.prototype.toString() method
// indicating that this object is a UsePseudoState object
get [Symbol.toStringTag]() { return 'UsePseudoState' },
};
// If the reactUseStateFn is provided, then the pseudoState will be initialized
if (reactUseStateFn && (typeof reactUseStateFn === 'function')) {
pseudoState.init(reactUseStateFn)
}
// The `if (mockState)` block is used to simulate the behavior of React's
// `useState` hook when it is not available. It is used for testing purposes
// only.
if (mockState) {
pseudoState.reactState = [
initialValue,
(newValue) => { pseudoState.reactState[0] = newValue }
]
pseudoState.reactState[mockedSymbol] = true
}
// Any reference to .length will still show three elements, even though we are going
// get tricky with the first element
const simulatedLiveState = [undefined, pseudoState.set.bind(pseudoState), pseudoState]
// Secretly maps the first element of the array to the pseudoState.get() method
// and any attempt to set the first element of the array to the pseudoState.set()
Object.defineProperty(simulatedLiveState, '0', accessorDescriptor(
()=>pseudoState.get(),
(value)=>pseudoState.set(value)
))
return simulatedLiveState
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment