https://github.com/bytebodger/use-synchronous-state
A custom React Hook that provides a synchronous way to read and update a state variable
https://github.com/bytebodger/use-synchronous-state
Last synced: 3 months ago
JSON representation
A custom React Hook that provides a synchronous way to read and update a state variable
- Host: GitHub
- URL: https://github.com/bytebodger/use-synchronous-state
- Owner: bytebodger
- License: mit
- Created: 2021-02-27T02:03:12.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2021-03-26T15:09:28.000Z (about 5 years ago)
- Last Synced: 2025-09-13T07:57:46.935Z (9 months ago)
- Language: JavaScript
- Size: 545 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# use-synchronous-state
A custom React Hook that provides a synchronous way to read and update a state variable. Becuase `useState()` is asynchronous, this means that you cannot set a state variable and then, very shortly thereafter, check that variable's value to perform further logic. Setting a state variable, then checking that variable immediately thereafter, will result in the code reading the old/previous state value. Consider the following example:
```javascript
const useHookWithState = () => {
const [firstFieldIsValid, setFirstFieldIsValid] = useState(false);
const [secondFieldIsValid, setSecondFieldIsValid] = useState(false);
const [formIsValid, setFormIsValid] = useState(false);
const validateFirstField = (newValue = '') => {
setFirstFieldIsValid(newValue !== '');
validateForm();
}
const validateForm = () => setFormIsValid(firstFieldIsValid && secondFieldIsValid);
const validateSecondField = (newValue = '') => {
setSecondFieldIsValid(newValue !== '');
validateForm();
}
return {
formIsValid,
validateFirstField,
validateSecondField,
};
}
const TestComponent = () => {
const hookWithState = useHookWithState();
useEffect(() => {
hookWithState.validateFirstField('an acceptable value');
hookWithState.validateSecondField('another acceptable value');
console.log(hookWithState.formIsValid); // returns FALSE,
// even though both fields have received valid values
}, []);
return <>>;
}
```
This code contains two validation functions that check two separate form fields. After checking any individual form field, the code then triggers `validateForm()`. But `validateForm()` does not operate properly because it is seeing the _old_ values for `firstFieldIsValid` and `secondFieldIsValid`.
To put this in simpler terms, the asynchronous nature of state can lead to code that contains this seemingly illogical result:
```javascript
const [value, setValue] = useState(true);
setValue(false);
if (value)
console.log(`this shouldn't be reached - but it is`);
```
## Methodology
This Hook works by storing _two_ values. The first is the "traditional" state value. The second is a plain ol' regular variable. Both variables contain the same value.
## Usage
```javascript
const [value, setValue] = useSynchronousState(true);
setValue(false);
if (value())
console.log(`this shouldn't be reached - and it isn't reached`);
```
## Methods
### useSynchronousState()
Like `useState()`, `useSynchronousState()` accepts an optional inital value for the state variable. Like `useState()`, `useSynchronousState()` returns an array of two values. Like `useState()`, the _second_ value is a setter function for the state variable.
_Unlike_ `useState()`, the _first_ value returned is not a simple value. Rather, it's a getter function. This means that every reference to the variable will be a function call, whether you're using the getter (read) or setter (write).
```javascript
const API = {
arguments: {
initialValue: {
optional,
format: any,
},
},
returns: [
get: Function,
set: Function,
],
}
```
**Examples:**
```javascript
const SomeComponent = () => {
const [counter, setCounter] = useSynchronousState(0);
return <>
Counter: {counter()}
setCounter(counter() + 1)}
>Increment
>;
}
```
The biggest syntactic difference between `useState()` and `useSynchronousState()` is that the `useSynchronousState()` value must always be referenced with a function call like this:
```javascript
console.log('counter value = ', counter());
```
_Not_ like this:
```javascript
console.log('counter value = ', counter);
```
This also means that there's no need to reference the alternative syntax for updating a state variable, because `counter()` will always returns _the most recent value_ of the `counter` variable.
It may be more intuitive for you to name the read function as a "getter" when it's destructured out of the `useSynchronousState()` call. That would look like this:
```javascript
const SomeComponent = () => {
const [getCounter, setCounter] = useSynchronousState(0);
return <>
Counter: {getCounter()}
setCounter(getCounter() + 1)}
>Increment
>;
}
```