react-data-table-component icon indicating copy to clipboard operation
react-data-table-component copied to clipboard

Editable input field within data table column

Open jacktiong92 opened this issue 5 years ago • 11 comments

Dear Sir or Madam,

First of all, thank you so much for creating and maintaining this library. It's really an awesome library.

Recently, I wanted to include editable input fields within data table column as shown in the image highlighted in yellow below. Screenshot 2019-12-23 at 9 30 28 PM

I tried a lot of different ways but I am getting an issue whereby the entire DataTable refreshes and I lost the focus of the input field whenever I update the state.

Below are the codes that I have tried:

// State
state = {
    itemDetailList: [
        {id: 0, item: 'Item', expDate: '01/01/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 21}},
        {id: 1, item: 'Item1', expDate: '01/02/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 22}},
        {id: 2, item: 'Item2', expDate: '01/03/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 203}},
        {id: 3, item: 'Item3', expDate: '01/04/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 205}},
        {id: 4, item: 'Item4', expDate: '01/05/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 5, item: 'Item5', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 6, item: 'Item6', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 7, item: 'Item7', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 8, item: 'Item8', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 9, item: 'Item9', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 10, item: 'Item10', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 11, item: 'Item11', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
        {id: 12, item: 'Item12', expDate: '01/06/2020', unitCost: 15, quantity: {pcs: 5, set: 10, ctn: 20}},
    ],
};
// Populate table columns
const itemDetailTableColumns = [
    {
        name: 'Item',
        selector: 'item',
        sortable: true,
    },
    {
        name: 'Expiry Date',
        selector: 'expDate',
        sortable: true,
    },
    {
        name: 'Unit Cost',
        selector: 'unitCost',
        sortable: true,
    },
    {
        name: 'Quantity',
        selector: 'quantity',
        cell:(row) => (
            <Aux>
                <Form.Group as={Row} className="align-items-center m-l-0">
                    <Col className="px-0">
                        <Form.Control 
                            type="text" 
                            key={"ctn" + row.id}
                            name={"ctn" + row.id}
                            id={"ctn" + row.id}
                            placeholder="" 
                            value={this.state.itemDetailList[row.id].quantity.ctn} 
                            onChange={(event) => {
                                var updatedItemDetailList = this.state.itemDetailList.slice()
                                updatedItemDetailList[row.id].quantity.ctn = event.target.value

                                this.setState({
                                    itemDetailList: updatedItemDetailList
                                })
                            }}
                        />
                    </Col>
                    <Form.Label column>
                        CTN
                    </Form.Label>
                </Form.Group>
                <Form.Group as={Row} className="align-items-center m-l-0">
                    <Col className="px-0">
                        <Form.Control 
                            type="text" 
                            key={"set" + row.id}
                            name={"set" + row.id}
                            id={"set" + row.id}
                            placeholder="" 
                            value={this.state.itemDetailList[row.id].quantity.set} 
                            onChange={(event) => {
                                console.log(row)
                                //console.log(event.target.value)

                                //console.log(this.updatedItemDetailList)

                                var updatedItemDetailList = this.state.itemDetailList.slice()
                                updatedItemDetailList[row.id].quantity.set = event.target.value

                                this.setState({
                                    itemDetailList: updatedItemDetailList
                                })
                            }}
                        />
                    </Col>
                    <Form.Label column>
                        SET
                    </Form.Label>
                </Form.Group>
                <Form.Group as={Row} className="align-items-center m-l-0">
                    <Col className="px-0">
                        <Form.Control 
                            type="text" 
                            key={"pcs" + row.id}
                            name={"pcs" + row.id}
                            id={"pcs" + row.id}
                            placeholder="" 
                            value={this.state.itemDetailList[row.id].quantity.pcs} 
                            onChange={(event) => {
                                console.log(row)
                                //console.log(event.target.value)

                                //console.log(this.updateditemDetailList)

                                var updatedItemDetailList = this.state.itemDetailList.slice()
                                updatedItemDetailList[row.id].quantity.pcs = event.target.value

                                this.setState({
                                    itemDetailList: updatedItemDetailList
                                })
                            }}
                        />
                    </Col>
                    <Form.Label column>
                        PCS
                    </Form.Label>
                </Form.Group>
            </Aux>
        ),
        sortable: true,
    },
];
// Data Table
<DataTable
    noHeader={true}
    columns={itemDetailTableColumns}
    data={this.state.itemDetailList}
    pagination={true}
    progressShowTableHead
/>

Thank you. Best Regards, Jack Tiong

jacktiong92 avatar Dec 23 '19 13:12 jacktiong92

Thanks! Glad you are finding RDT useful 😄

So, it looks like you're updating data={this.state.itemDetailList} every time you type into a field. This will cause RDT to re-render. This is by design and how React works.

Instead of directly updating state on your inputs just have a save button on your row or cell that does the state update instead. Also when you update your state you may want to look into some immutable patterns for updating state as you are currently mutating your state which React will not work well with. https://daveceddia.com/why-not-modify-react-state-directly/

I hope this helps and good luck!

jbetancur avatar Jan 03 '20 15:01 jbetancur

One final thing that may be helpful. This is a different table library 😬 but you can see here how they are handling row updates

https://material-ui.com/components/tables/

I'm going to re-open this as documentaion and I'll also see about adding an example this to the storybooks for inline editing

jbetancur avatar Jan 03 '20 15:01 jbetancur

Thank you so much @jbetancur I tried a lot of different libraries (including material-ui table) and it seems like only react-table (https://github.com/tannerlinsley/react-table) library can fulfill most of my requirements as it allows a certain column or a certain table cell to be disabled and the state will be updated each time when an input loss its focus. Pretty cool I must say.

jacktiong92 avatar Jan 03 '20 15:01 jacktiong92

Actually, I think this may also be a potential feature for a new onRowUpdate prop to make things easier

jbetancur avatar Jan 03 '20 15:01 jbetancur

https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/editable-data

Interesting. I'll see about feature flagging this in the future for editable columns!

jbetancur avatar Jan 03 '20 15:01 jbetancur

@jbetancur Is it possible to use select in editable columns?

MatheusLima7 avatar Apr 27 '20 15:04 MatheusLima7

Thanks! Glad you are finding RDT useful 😄

So, it looks like you're updating data={this.state.itemDetailList} every time you type into a field. This will cause RDT to re-render. This is by design and how React works.

Instead of directly updating state on your inputs just have a save button on your row or cell that does the state update instead. Also when you update your state you may want to look into some immutable patterns for updating state as you are currently mutating your state which React will not work well with. daveceddia.com/why-not-modify-react-state-directly

I hope this helps and good luck!

@jbetancur making save button makes ui not reactive. Sometimes it's unacceptable. Is there any way to make it so, that cells are updated, rather then re-mounted?

davidarny avatar May 02 '20 10:05 davidarny

I have a similar problem. I have inputs in the row and every time I write a text it loses focus after the first character. The same code works for reactstrap table or by using simple divs. Placing a save button is unacceptable in my case

ghost avatar May 09 '20 02:05 ghost

@jbetancur Hey, is there any updates on this feature?

AaronPowell96 avatar Aug 17 '20 21:08 AaronPowell96

this feature would be really nice

luanpanno avatar Dec 10 '20 14:12 luanpanno

Not sure if its helpful to anyone, I ended up using onBlur to apply the changes to state once they had clicked away, one issue is if they click on a button a the first next click, it wont trigger a button click but instead just the blur. It seems to work "ok" though.

AaronPowell96 avatar Dec 10 '20 15:12 AaronPowell96