Files
dashfront/src/store/articleAutomat.js
2026-09-19 18:33:27 +02:00

125 lines
3.1 KiB
JavaScript

import { Automat, fetchJson } from 'react-automat';
import { article } from '../api/index';
import { requireObject, requireDefined } from '../utils/apiShape';
/**
* Article automat store.
* Registered as window.artikel via the name option.
* State shape: { [sku: string]: article | null }
*
* Usage in PureComponent:
* ```jsx
* import articleAutomat, { createArticleSelector } from '../../store/articleAutomat';
*
* class ArticleView extends PureComponent {
* constructor(props) {
* super(props);
* const sku = props.sku;
* const currentArticle = articleAutomat.state[sku] ?? null;
* this.state = { article: currentArticle };
*
* if (!currentArticle) {
* articleAutomat.actions.loadArticle(sku).catch(console.error);
* }
* }
*
* componentDidMount() {
* this.subscribeToArticle();
* }
*
* componentDidUpdate(prevProps) {
* if (prevProps.sku !== this.props.sku) {
* const currentArticle = articleAutomat.state[this.props.sku] ?? null;
* this.setState({ article: currentArticle });
*
* if (!currentArticle) {
* articleAutomat.actions.loadArticle(this.props.sku).catch(console.error);
* }
*
* this.subscribeToArticle();
* }
* }
*
* subscribeToArticle() {
* if (this.unsub) this.unsub();
* const selector = createArticleSelector(this.props.sku);
* this.unsub = articleAutomat.subscribe(this, (state) => ({
* article: selector(state),
* }));
* }
*
* componentWillUnmount() {
* if (this.unsub) {
* this.unsub();
* this.unsub = null;
* }
* }
*
* render() {
* const { article } = this.state;
* if (!article) return null;
* return <div>{article.name}</div>;
* }
* }
* ```
*/
const articleAutomat = new Automat(
{},
{
/**
* Load article by SKU and update the store.
* @param {string} sku
*/
async loadArticle(sku) {
const res = await article.getBySku(sku);
requireObject(res, 'GET /api/article/:sku');
requireObject(res.article, 'article');
requireDefined(res.article.sku, 'article.sku');
articleAutomat.setState({
[sku]: res.article,
});
return res.article;
},
/**
* Update article state after a successful mutation.
* @param {object} article
*/
setArticle(article) {
requireObject(article, 'article');
requireDefined(article.sku, 'article.sku');
articleAutomat.setState({
[article.sku]: article,
});
},
/**
* Clear article from store (e.g., after deletion).
* @param {string} sku
*/
clearArticle(sku) {
articleAutomat.setState({
[sku]: null,
});
},
},
{
name: 'artikel',
}
);
/**
* Create a selector function for a specific SKU.
* This avoids closure issues when the SKU changes.
* @param {string} sku - The article SKU
* @returns {(state: object) => object|null} Selector function
*/
export function createArticleSelector(sku) {
return (state) => state[sku] ?? null;
}
export default articleAutomat;