125 lines
3.1 KiB
JavaScript
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;
|