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
{article.name}
; * } * } * ``` */ 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;