I think this is a simple enough question. I am trying to create a material ui grid in react and be able to add rows to it. I've tried several variations of patterns and nothing is working for me. I'm pasting the sample below, and you can find the code here.
import React from "react";
import "./styles.css";
import { observer, useObservable } from "mobx-react-lite";
import MaterialTable from "material-table";
import Button from "@material-ui/core/Button";
const columns = [
{ title: "First Name", field: "name" },
{ title: "Last Name", field: "surname" },
{ title: "Year of Birth", field: "birthYear", type: "numeric" },
{
title: "City of Birth",
field: "birthCity",
lookup: {
34: "Haughton",
63: "San Diego",
88: "Henryetta"
}
}
];
const initialData = [
{ name: "Dak", surname: "Prescott", birthYear: 1993, birthCity: 34 }
];
const addData = [
{ name: "Troy", surname: "Aikman", birthYear: 1966, birthCity: 88 },
{ name: "Tony", surname: "Romo", birthYear: 1980, birthCity: 63 }
];
const App = observer(() => {
const store = useObservable({
data: initialData,
index: 0,
addRow() {
if (store.index < store.data.length) {
store.data.push(addData[store.index++]);
}
}
});
return (
<div className="App">
<MaterialTable
columns={columns}
data={store.data}
title="Sample Material Table"
/>
<Button onClick={() => store.addRow}>Add Row</Button>
</div>
);
});
export default App;
This is just one attempt. My real Mobx stores I create in separate files and each attribute that is observable I annotate with @observable, but at this point I'll accept absolutely anything that works. I've gotten Mobx stores working with native values (strings and numbers) but not with an array of objects. I hope I'm just missing some small nuance.
You're right about the convention for modifying stores - generally, it's better to create a new array or object and spread your old data into the new one while you merge your new data in.
I think in your case, you just have a typo in your onClick
handler. You're not invoking it in the Button element - you're running an arrow function, but inside you're not calling the addRow
method! I've rewritten it for you and also switched to the useLocalStore
hook as that's the recommended one to use these days.
This is also a good opportunity to review the syntax for calling functions from onClick
attributes in React.
onClick={handleClick}
// Passing a reference to a function, React will automatically call this function when the element is clicked. You don't need to call it manually. In other words, putting parenths here is wrong.
onClick={() => handleClick}
// Creating an inline arrow function, React will automatically run this function like above. It will execute each line just like a normal function. It will reach the identifier handleClick
and do nothing more because handleClick is not being called within the new arrow function you've created.
onClick={() => handleClick()}
// Same as above in that you're creating an inline arrow function. React will run this arrow function like above. It will execute each line and then call your function because you've invoked it with parenths.
Hope this helps!
By the way, you'll want to verify that the data source you're getting new rows from is able to accept the index you're passing in. Right now if you click the button more than twice you'll get an index out of bounds error. But I'm assuming you're taking care of that part already so I didn't change that part of your code.
import React from "react";
import "./styles.css";
import { useObserver } from "mobx-react-lite";
import MaterialTable from "material-table";
import Button from "@material-ui/core/Button";
import { useLocalStore } from "mobx-react";
const columns = [
{ title: "First Name", field: "name" },
{ title: "Last Name", field: "surname" },
{ title: "Year of Birth", field: "birthYear", type: "numeric" },
{
title: "City of Birth",
field: "birthCity",
lookup: {
34: "Haughton",
63: "San Diego",
88: "Henryetta"
}
}
];
const initialData = [
{ name: "Dak", surname: "Prescott", birthYear: 1993, birthCity: 34 }
];
const addData = [
{ name: "Troy", surname: "Aikman", birthYear: 1966, birthCity: 88 },
{ name: "Tony", surname: "Romo", birthYear: 1980, birthCity: 63 }
];
const App = () => {
const store = useLocalStore(() => ({
data: initialData,
index: 0,
addRow() {
if (this.index < this.data.length) {
this.data = [...this.data, addData[this.index++]];
}
}
}));
return useObserver(() => (
<div className="App">
<MaterialTable
columns={columns}
data={store.data}
title="Sample Material Table"
/>
<Button onClick={store.addRow}>Add Row</Button>
</div>
));
};
export default App;