This commit is contained in:
Martti Malmi 2021-09-27 15:29:39 +03:00
parent 5c231b32d7
commit ad5e477acc

View File

@ -12,7 +12,7 @@ image: https://siriusbusiness.fi/assets/images/posts/painless.jpg
Fed up with writing a ton of Redux boilerplate just to make a form input editable? Fed up with writing a ton of Redux boilerplate just to make a form input editable?
Theres a better alternative: [Gun.js](https://github.com/amark/gun). It makes state synchronization and persistence super easy. Theres a better alternative: [Gun.js](https://github.com/amark/gun). It makes state synchronization and persistence a breeze.
First, initialize Gun with options that make sure the state is synced with localStorage only (not with peers). First, initialize Gun with options that make sure the state is synced with localStorage only (not with peers).
```jsx ```jsx
@ -28,10 +28,8 @@ Then create a React component that uses the state:
```jsx ```jsx
class TextInput extends React.Component { class TextInput extends React.Component {
constructor() { state = {text:''};
super();
this.state = {text:''};
}
componentDidMount() { componentDidMount() {
State.get('text').on(text => this.setState({text})); State.get('text').on(text => this.setState({text}));
} }
@ -53,3 +51,55 @@ class TextInput extends React.Component {
Now you have a text form that syncs its content across component instances and persists it in localStorage. Now you have a text form that syncs its content across component instances and persists it in localStorage.
See the working example on [Codepen](https://codepen.io/mmalmi/pen/VwWVdKG). See the working example on [Codepen](https://codepen.io/mmalmi/pen/VwWVdKG).
However, if we now unmount the component and write to `State.get('text')` elsewhere, we'll get the warning: `Can't call setState on an unmounted component. This is a no-op, but it indicates a memory leak in your application.`
We can avoid that by saving our state subscriptions and unsubscribing them in `componentWillUnmount()`. Here's a helper class for that:
```jsx
class BaseComponent extends React.Component {
subscriptions = {};
subscribe(callback, path) {
return (value, key, x, subscription, f) => {
if (this.unmounted) {
subscription && subscription.off();
return;
}
this.subscriptions[path || key] = subscription;
callback(value, key, x, subscription, f);
}
}
inject(name, path) {
return this.subscribe((v,k) => {
const newState = {};
newState[name || k] = v;
this.setState(newState);
}, path);
}
componentWillUnmount() {
this.unmounted = true;
Object.keys(this.subscriptions).forEach(k => {
const subscription = this.subscriptions[k];
subscription && subscription.off();
delete this.subscriptions[k];
});
}
}
```
Now we can extend BaseComponent, which takes care of injecting values to the component's state and unsubscribing on unmount.
```jsx
class TextInput extends BaseComponent {
// ...
componentDidMount() {
State.get('text').on(this.inject());
}
// ...
}
```
[Codepen](https://codepen.io/mmalmi/pen/MWozPZE)