Home > Back-end >  React Inserting a compoent into a table between 2 rows dynamically
React Inserting a compoent into a table between 2 rows dynamically

Time:11-18

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.

  • Related