In React I have a table that lists rows of data and each row has a button that I want to insert a row after it and add component that allows the user to fill inform data and click save and then delete the form row.
This is code that I have tried.
addRow = (idx) => {
const table = document.getElementById("allocatorTable");
const row = table.insertRow(idx 1);
row.className = "editorRow";
const cell1 = row.insertCell(0);
cell1.colspan = "10";
cell1.appenChild(<TransactionRuleEditor />);
};
Of course the <TransactionRuleEditor /> append wont work but it gives you the idea of what i am trying to achieve.
I have also tried adding the form to each row and hiding them. But of course you cant have more than one form on a page.
Any ideas plz? Malcolm
CodePudding user response:
When you're using React (or any other MVC-like framework), you don't work directly with the DOM like that, you take a different mindset: You change the state of what's being rendered, and then the framework does the rendering. In this case, you might have the component providing the table row have a state where it actually provides two rows (wrapped in a fragment) when the button has been pressed.
Here's a simplified exmaple:
const {useState, Component} = React;
class Row extends Component {
constructor(props) {
super(props);
this.state = {
adding: false,
};
this.onAdd = this.onAdd.bind(this);
this.onDone = this.onDone.bind(this);
}
onAdd() {
this.setState({adding: true});
}
onDone() {
// (Obviously you'd do more here)
this.setState({adding: false});
}
render() {
const {adding} = this.state;
const {data} = this.props;
const row = <tr>
<td>{data.a}</td>
<td>{data.b}</td>
<td>{data.c}</td>
<td>{!adding &&
<input type="button" onClick={this.onAdd} value="Add" />
}</td>
</tr>;
if (!adding) {
return row;
}
return <React.Fragment>
{row}
<tr>
<td colSpan={3}>
The inputs go here.
</td>
<td>
<input type="button" onClick={this.onDone} value="Done" />
</td>
</tr>
</React.Fragment>;
}
}
// Or with hooks:
// const Row = ({data}) => {
// const [adding, setAdding] = useState(false);
// const row = <tr>
// <td>{data.a}</td>
// <td>{data.b}</td>
// <td>{data.c}</td>
// <td>{!adding &&
// <input type="button" onClick={() => setAdding(true)} value="Add" />
// }</td>
// </tr>;
// if (!adding) {
// return row;
// }
// // (You might memoize the callbacks via `useCallback`)
// return <React.Fragment>
// {row}
// <tr>
// <td colSpan={3}>
// The inputs go here.
// </td>
// <td>
// <input type="button" onClick={() => /* Obviously you'd do more here */ setAdding(false)} value="Done" />
// </td>
// </tr>
// </React.Fragment>;
// }
const Example = () => {
const [items] = useState([
{key: 1, a: "a1", b: "b1", c: "c1"},
{key: 2, a: "a2", b: "b2", c: "c2"},
{key: 3, a: "a3", b: "b3", c: "c3"},
]);
return <table>
<tbody>
{items.map(item => <Row key={item.key} data={item} />)}
</tbody>
</table>;
};
ReactDOM.render(<Example />, document.getElementById("root"));
td {
width: 70px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
(That example uses <React.Fragment>
because the version of Babel in Stack Snippets is very out of date, but in your real code you can use <>
instead.)
That's just a sketch, of course. But it gives you an idea of how state can be used to handle this modality.