i'm creating a table where once you click a row, a popup modal opens up allowing you to update the content of the row. the modal's closing sequence updates firestore of the changes and also activates a props function which updates the changes into the above component.
closing the modal does not update the table (I've come to realize it does'nt cause a component re-reder at all...)
components:
father to the table:
import React, { Component } from "react";
import { db } from "../../firebase/firebase";
import InventoryTable from "./Table/InventoryTable";
class InventoryPage extends Component {
constructor(props) {
super(props);
this.state = {
filterGroups: [],
data: [],
originalData: [],
};
this.modalUpdate = this.modalUpdate.bind(this);
this.filterGroupToggle = this.filterGroupToggle.bind(this);
}
modalUpdate(modifiedRow) {
let newData = this.state.data;
let oldRow = newData.filter((row) => {
return row.id === modifiedRow.id;
});
let oldRowIndex = newData.indexOf(...oldRow);
newData.splice(oldRowIndex, 1, modifiedRow);
console.log(newData);
this.setState({
data: newData,
});
}
filterGroupToggle(index) {
// Switch the groupFilter that was clicked to ON/OFF
let newFilterGroups = this.state.filterGroups;
newFilterGroups[index].on = !newFilterGroups[index].on;
this.setState({
filterGroups: newFilterGroups,
});
const included = this.state.filterGroups
.filter((group) => {
return group.on;
})
.map((group) => {
return group.name;
});
if (included.length) {
console.log(included);
let newData = this.state.data;
newData = newData.filter((row) => {
return included.includes(row.group);
});
this.setState({
data: newData,
});
} else {
this.setState({
data: this.state.originalData,
});
}
}
componentDidMount() {
db.collection("inventoryData")
.doc("filtering")
.get()
.then((doc) => {
let newFilterGroups = [];
doc.data().groups.map((group) => {
newFilterGroups.push({
name: group,
on: false,
});
return this.setState({
filterGroups: newFilterGroups,
});
});
});
db.collection("inventory")
.get()
.then((quertSnapshot) => {
let data = [];
quertSnapshot.forEach((doc) => {
data.push({
id: doc.id,
...doc.data(),
});
});
this.setState({
data,
originalData: data,
});
});
}
render() {
console.log("InventoryPage render");
const { filterGroups } = this.state;
return (
<>
<div className="container">
<div className="h1 mb-4 mt-2">Inventory</div>
{filterGroups.length ? (
<div className="row justify-content-around">
{filterGroups.map((group, index) => {
return (
<div
key={index}
className={
"col-2 mx-1 mb-3 toggle-buttons " +
(group.on ? "ON" : null)
}
onClick={() => {
this.filterGroupToggle(index);
}}
>
{group.name}
</div>
);
})}
</div>
) : (
<div className="h4">Loading Filtering Groups...</div>
)}
</div>
{this.state.data.length ? (
<InventoryTable db={this.state.data} modalUpdate={this.modalUpdate} />
) : (
<div className="container h4 mt-3">No items to show...</div>
)}
</>
);
}
}
export default InventoryPage;
Table component:
import React, { useState, useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSortUp, faSortDown } from "@fortawesome/free-solid-svg-icons";
import { useTable, useSortBy, useGlobalFilter } from "react-table";
import ManualEditModal from "./ManualEditModal";
import GlobalFilter from "./GlobalFilter";
const InventoryTable = ({ db, modalUpdate }) => {
const [modalData, setModalData] = useState(null);
const [showModal, setShowModal] = useState(false);
useEffect(() => {
console.log("modal data effect");
if (modalData) {
setShowModal(true);
} else {
setShowModal(false);
}
}, [modalData]);
const handleCloseModal = () => {
console.log("close modal");
// setShowModal(false);
setModalData(null);
};
// i'm supposed to use useMemo here,
// but i couldn't find a way to rerender the table
// upon props change aside from deleting useMemo
const data = React.useMemo(db, []);
const columns = React.useMemo(
() => [
{
Header: "Info",
columns: [
{
Header: "Name",
accessor: "name", // accessor is the "key" in the data
},
{
Header: "Type",
accessor: "type",
},
],
},
{
Header: "Stock",
columns: [
{
Header: "Current Stock",
accessor: "stock",
},
{
Header: "Awaiting Arrival",
accessor: "await_arrival",
},
{
Header: "Awaiting Order",
accessor: "await_order",
},
],
},
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state,
setGlobalFilter,
} = useTable({ columns, data }, useGlobalFilter, useSortBy);
const { globalFilter } = state;
return (
<div className="container mt-3">
{showModal ? (
<div className="editRowModal">
<ManualEditModal
show={showModal}
data={modalData}
onHide={handleCloseModal}
modalUpdate={modalUpdate}
/>
</div>
) : null}
<div className="form-group mb-3">
<GlobalFilter filter={globalFilter} setFilter={setGlobalFilter} />
</div>
<table {...getTableProps()} className="table table-bordered">
<thead className="thead-dark">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps(column.getSortByToggleProps())}
scope="col"
>
{column.render("Header")}
<span className="sorting-icon">
{column.isSorted ? (
column.isSortedDesc ? (
<FontAwesomeIcon icon={faSortUp} className="ml-2" />
) : (
<FontAwesomeIcon icon={faSortDown} className="ml-2" />
)
) : (
""
)}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
return (
<tr
{...row.getRowProps()}
onClick={() => {
setModalData(row.original);
}}
>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
);
};
export default InventoryTable;
modal Component:
import React, { useState } from "react";
import { Modal, Button } from "react-bootstrap";
import { db } from "../../../firebase/firebase";
const ManualEditModal = ({ show, data, onHide, modalUpdate }) => {
const [stock, setStock] = useState(data ? data.stock : -1);
// update the database on the new stock value
const onSave = () => {
if (isNaN(Number(stock)) || stock < 0) {
alert("Invalid value");
return;
}
db.collection("inventory")
.doc(data.id)
.update({
stock: Number(stock),
})
.then(() => {
// call a function to update the state (and therefore the table) on the new values.
data.stock = Number(stock);
modalUpdate(data);
onHide();
});
};
if (show && data) {
return (
<Modal show={show} onHide={onSave}>
<Modal.Header closeButton>
<Modal.Title>{data.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="input-group">
<div className="input-group-prepend">
<span className="input-group-text">Current Stock</span>
</div>
<input
className="form-control"
type="number"
value={stock}
onChange={(e) => {
setStock(e.target.value);
}}
/>
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={onSave}>
Save & Close
</Button>
<Button variant="secondary" onClick={onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
);
} else {
return null;
}
};
export default ManualEditModal;
thank you in advance for the help, i hope this can be done... on this problem for 2 weeks!!
Update: As of now, i tried (with no success):
- removing the useMemo hook from the data constant to the table, making it rerender more often but it does not seem to cut it.
- Passing the data from props as a new array and spreading the old one in to it.