Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndreiTelteu/226d2ab9722f02fc012dba5592e3a187 to your computer and use it in GitHub Desktop.
Save AndreiTelteu/226d2ab9722f02fc012dba5592e3a187 to your computer and use it in GitHub Desktop.
React useBetterState with mutable state, callback after change, and promise. Just like the old this.setState() Class Component days. DEMO: https://stackblitz.com/edit/react-usebetterstate

useBetterState

I'm trying to bring back the magic of setState(value, callback) from React Class Component days, while adding more functionality on top, like promises and mutable state.

To start using it, initialize it with:

const [state, setState] = useBetterState({
  count: 0,
  name: '',
});

There are 4 ways to use it while using the updated state right after.

1. Callback

The old way from React Class Components. Why did they get rid of it ? It was soo useful.

setState((i) => {
  i.count++;
}, (state) => {
  console.log('latest count: ', state.count);
});

2. Mutable state

AKA: Just change the state. To use the updated value, use state._current.

state.count++;
console.log('latest count: ', state._current.count);

3. Promise with await

Async / Await the setState !

let state = await setState((i) => {
  i.count++;
});
console.log('latest count: ', state.count);

4. Promise with .then

Classic promise with then callback.

setState((i) => {
  i.count++;
}).then((state) => {
  console.log('latest count: ', state.count);
});

Bonus: cloned by default

Just like the old this.setState() cloned the state object and merged in the new state, this better state does the same. This means you can change any state without cloning the current state.

setState({ name: 'Andrei' }) // state.count remains the same
import { useState } from 'react';
const useBetterState = (initialValue) => {
const [value, setValue] = useState(initialValue);
let latestValue = { state: { ...initialValue } };
const setValuePromise = (newValue, callback = null) => {
const promise = new Promise((resolve) => {
setValue((prevValue) => {
let cloned = { ...prevValue };
let realValue =
typeof newValue == 'function' ? newValue(cloned) : newValue;
if (typeof realValue == 'undefined') {
realValue = cloned;
}
realValue = { ...cloned, ...realValue };
latestValue.state = realValue;
resolve(realValue);
return realValue;
});
});
if (callback != null) {
promise.then((v) => callback(v));
}
return promise;
};
const betterValue = new Proxy(
{},
{
get(target, key) {
if (key === '_current') return latestValue.state;
return value[key];
},
set(target, key, newValue) {
const v = { ...value };
v[key] = newValue;
latestValue.state = v;
setValue(v);
return true;
},
}
);
return [
typeof initialValue == 'object' ? betterValue : value,
setValuePromise,
];
};
export default useBetterState;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment