From b6aa5a036c2f965e2ca2980813ee63ffd2df97ad Mon Sep 17 00:00:00 2001
From: meskio <meskio@sindominio.net>
Date: Wed, 23 Sep 2020 17:00:16 +0200
Subject: [PATCH] Factor out the fetcher

---
 src/Dashboard.js   | 50 ++++++++--------------------------
 src/Fetcher.js     | 68 ++++++++++++++++++++++++++++++++++++++++++++++
 src/ProductList.js | 67 ++++++++++++---------------------------------
 src/member.js      | 66 ++++++++++++--------------------------------
 4 files changed, 113 insertions(+), 138 deletions(-)
 create mode 100644 src/Fetcher.js

diff --git a/src/Dashboard.js b/src/Dashboard.js
index 4273d3f..0752448 100644
--- a/src/Dashboard.js
+++ b/src/Dashboard.js
@@ -1,6 +1,7 @@
 import React from 'react';
+import { Container } from 'react-bootstrap';
 import AuthContext from './AuthContext';
-import { Container, Spinner, Alert, Row } from 'react-bootstrap';
+import Fetcher from './Fetcher';
 import printMoney from './util';
 
 class Dashboard extends React.Component {
@@ -10,49 +11,20 @@ class Dashboard extends React.Component {
         super(props);
         this.state = {
             name: null,
-            balance: null,
-            isLoading: false,
-            error: null
+            balance: null
         };
     }
 
-    componentDidMount() {
-        this.setState({ isLoading: true });
-
-        fetch('/api/member/'+this.context.num.toString(),
-              { headers: { 'x-authentication': this.context.token }})
-            .then(response => {
-                if (!response.ok) {
-                    throw new Error(response.status.toString()+' '+response.statusText);
-                }
-                return response.json();
-            })
-            .then(member => this.setState({ balance: member.balance, name: member.name, isLoading: false }))
-            .catch(e => this.setState({ error: e.message, isLoading: false }));
-    }
-
     render() {
-        if (this.state.isLoading) {
-            return (
-              <Row className="justify-content-md-center">
-                <Spinner animation="border" />
-              </Row>
-            );
-        }
-
-        if (this.state.error != null) {
-            return (
-                <Alert variant="danger">
-                   Ha ocurrido un error cargando tu saldo: {this.state.error}
-                </Alert>
-            );
-        }
-
         return (
-            <Container>
-                <h1>{this.state.name}</h1>
-                <p>Saldo: {printMoney(this.state.balance)}</p>
-            </Container>
+            <Fetcher
+                    url={"/api/member/"+this.context.num.toString()}
+                    onFetch={member => this.setState({ balance: member.balance, name: member.name})} >
+                <Container>
+                    <h1>{this.state.name}</h1>
+                    <p>Saldo: {printMoney(this.state.balance)}</p>
+                </Container>
+            </Fetcher>
         );
     }
 }
diff --git a/src/Fetcher.js b/src/Fetcher.js
new file mode 100644
index 0000000..22a563b
--- /dev/null
+++ b/src/Fetcher.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import { Spinner, Alert, Row } from 'react-bootstrap';
+import AuthContext from './AuthContext';
+
+class Fetcher extends React.Component {
+    static contextType = AuthContext;
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            isLoading: false,
+            error: null
+        };
+    }
+
+    componentDidMount() {
+        this.setState({ isLoading: true });
+        this.fetch();
+        this.timerID = setInterval(
+            () => this.fetch(),
+            10000  // every 10 seconds
+        );
+    }
+
+    compomentWillUnmount() {
+        clearInterval(this.timerID);
+    }
+
+    fetch() {
+        fetch(this.props.url, { headers: { 'x-authentication': this.context.token } })
+            .then(response => {
+                if (!response.ok) {
+                    throw new Error(response.status.toString()+' '+response.statusText);
+                }
+                return response.json();
+            })
+            .then(data => {
+                if (this.state.isLoading) {
+                    this.setState({ isLoading: false });
+                }
+                this.props.onFetch(data);
+            })
+            .catch(e => this.setState({ error: e.message, isLoading: false }));
+    }
+
+    render() {
+        if (this.state.isLoading) {
+            return (
+              <Row className="justify-content-md-center">
+                <Spinner animation="border" />
+              </Row>
+            );
+        }
+
+        if (this.state.error != null) {
+            console.log(this.state.error);
+            return (
+                <Alert variant="danger">
+                   Ha ocurrido un error cargando datos: {this.state.error}
+                </Alert>
+            );
+        }
+
+        return this.props.children;
+    }
+}
+
+export default Fetcher;
diff --git a/src/ProductList.js b/src/ProductList.js
index 2eb7f97..5593ab7 100644
--- a/src/ProductList.js
+++ b/src/ProductList.js
@@ -1,52 +1,17 @@
 import React from 'react';
-import { Table, Spinner, Alert, Row } from 'react-bootstrap';
-import AuthContext from './AuthContext';
+import { Table } from 'react-bootstrap';
+import Fetcher from './Fetcher';
 import printMoney from './util';
 
 class ProductList extends React.Component {
-    static contextType = AuthContext;
-
     constructor(props) {
         super(props);
         this.state = {
             products: [],
-            isLoading: false,
-            error: null
         };
     }
 
-    componentDidMount() {
-        this.setState({ isLoading: true });
-
-        fetch('/api/product', { headers: { 'x-authentication': this.context.token }})
-            .then(response => {
-                if (!response.ok) {
-                    throw new Error(response.status.toString()+' '+response.statusText);
-                }
-                return response.json();
-            })
-            .then(products => this.setState({ products, isLoading: false }))
-            .catch(e => this.setState({ error: e.message, isLoading: false }));
-    }
-
     render() {
-        if (this.state.isLoading) {
-            return (
-              <Row className="justify-content-md-center">
-                <Spinner animation="border" />
-              </Row>
-            );
-        }
-
-        if (this.state.error != null) {
-            console.log(this.state.error);
-            return (
-                <Alert variant="danger">
-                   Ha ocurrido un error cargando el listado de productos: {this.state.error}
-                </Alert>
-            );
-        }
-
         const entries = this.state.products.map((product) => {
             return (
                 <tr key={product.code}>
@@ -59,19 +24,21 @@ class ProductList extends React.Component {
         });
 
         return (
-            <Table striped bordered hover>
-                <thead>
-                    <tr>
-                        <th>codigo</th>
-                        <th>Nombre</th>
-                        <th>Precio</th>
-                        <th>Existencias</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    {entries}
-                </tbody>
-            </Table>
+            <Fetcher url="/api/product" onFetch={products => this.setState({ products })} >
+                <Table striped bordered hover>
+                    <thead>
+                        <tr>
+                            <th>codigo</th>
+                            <th>Nombre</th>
+                            <th>Precio</th>
+                            <th>Existencias</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {entries}
+                    </tbody>
+                </Table>
+            </Fetcher>
         );
     }
 }
diff --git a/src/member.js b/src/member.js
index 6fda8cc..b790c3b 100644
--- a/src/member.js
+++ b/src/member.js
@@ -1,52 +1,18 @@
 import React from 'react';
-import { Table, Spinner, Alert, Row } from 'react-bootstrap';
-import AuthContext from './AuthContext';
+import { Table } from 'react-bootstrap';
+import Fetcher from './Fetcher';
 import printMoney from './util';
 
 class MemberList extends React.Component {
-    static contextType = AuthContext;
 
     constructor(props) {
         super(props);
         this.state = {
             members: [],
-            isLoading: false,
-            error: null
         };
     }
 
-    componentDidMount() {
-        this.setState({ isLoading: true });
-
-        fetch('/api/member', { headers: { 'x-authentication': this.context.token }})
-            .then(response => {
-                if (!response.ok) {
-                    throw new Error(response.status.toString()+' '+response.statusText);
-                }
-                return response.json();
-            })
-            .then(members => this.setState({ members, isLoading: false }))
-            .catch(e => this.setState({ error: e.message, isLoading: false }));
-    }
-
     render() {
-        if (this.state.isLoading) {
-            return (
-              <Row className="justify-content-md-center">
-                <Spinner animation="border" />
-              </Row>
-            );
-        }
-
-        if (this.state.error != null) {
-            console.log(this.state.error);
-            return (
-                <Alert variant="danger">
-                   Ha ocurrido un error cargando el listado de socias: {this.state.error}
-                </Alert>
-            );
-        }
-
         const entries = this.state.members.map((member) => {
             return (
                 <tr key={member.num}>
@@ -59,19 +25,21 @@ class MemberList extends React.Component {
         });
 
         return (
-            <Table striped bordered hover>
-                <thead>
-                    <tr>
-                        <th>Numero</th>
-                        <th>Nombre</th>
-                        <th>Email</th>
-                        <th>Saldo</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    {entries}
-                </tbody>
-            </Table>
+            <Fetcher url="/api/member" onFetch={members => this.setState({ members })} >
+                <Table striped bordered hover>
+                    <thead>
+                        <tr>
+                            <th>Numero</th>
+                            <th>Nombre</th>
+                            <th>Email</th>
+                            <th>Saldo</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        {entries}
+                    </tbody>
+                </Table>
+            </Fetcher>
         );
     }
 }
-- 
GitLab