0

I am using fluent-ui's DetailsList to render a table. I am trying to convert this into a simple editable grid. The 1st column is a readonly string, the 2nd column is an editable TextField and the 3rd column is the delete button. I have provided ItemColumn rendering function so that each column can be rendered as desired. See the following demo:

https://codesandbox.io/p/sandbox/detailslist-delete-broken-gfnrgn?file=%2Fsrc%2Findex.tsx%3A74%2C43

I have provided 2 rows, with the first row having the default value in the textfield of "Blue", and the second row having blank.

The problem: when I click on the delete button in the first row, the color is preserved at that index. So now it looks like Beagal's favorite color is "Blue", when it was in fact John's. This only seems to be a problem with Input columns, and not readonly columns like "Name" in this example, which is why "Beagal" correctly shifts up upon delete of the row above it. This is extra confusing because when I log the listItems state variable, it looks correct, so it's like the grid isn't properly re-rendering the TextField or something.

How to I ensure that when I delete a row, the TextField doesn't show the data from the row below it?

1 Answer 1

1

The <TextField> is using a defaultValue. This means the component is "uncontrolled" in the sense it manages its state internally. Seemingly because of a slightly unexpected quirk of Fluent's behaviour, when the defaultValue changes it is ignored and it retains the internal copy it already has.

You can demonstrate this by setting a key that forces the text field to remount completely when the row deletion causes that item to be in a new position:

<TextField key={item.name + index} defaultValue={item.favColor}></TextField>

This causes all the TextFields to essentially be removed and readded -- circumventing the problem since the TextField will be forced to grab the new initial value.

But I suspect you actually wanted to be editing the dataset itself with the new value. I.e. using TextField as a controlled component via the value prop. The onChange prop here then actually updates the favColor field for the data for the row in question (with the new value, each time a keystroke happens) by deep updating listItems.

        case "favColorColumn": {
          return (
            <TextField
              onChange={(e, newVal) => {
                setListItems((prevListItems) =>
                  Object.assign([], prevListItems, {
                    [index]: { ...prevListItems[index], favColor: newVal },
                  })
                );
              }}
              value={item.favColor}
            ></TextField>
          );
        }
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you! I was trying to set the key on the DetailsList and didn't realize I had to set it on the TextField itself. That being said, the "controlled" version is what I was actually looking for. I created an updated link where I implemented that: codesandbox.io/p/sandbox/…
Do you know why when I change the type to "Number" it stops working? E.g. "Fav color" becomes "fav number". If I type in "123" into the first row and delete it, I'm left with single character "1". Curiously, if I type in just a single character "1", I have to backspace twice to delete it. See: codesandbox.io/p/sandbox/…
Its because of needing to detect more precisely what a falsey value is. You should use value={item.favNumber === undefined ? "" : String(item.favNumber)} and inside the object assign favNumber: newVal !== "" ? Number(newVal) : undefined,
Thought process. item.favNumber?.toString() would return undefined when item.favNumber is undefined. You want value to always be a string. So we detect this scenario and set empty string. For the setting part, you should detect newVal is literally an empty string to avoid any weird equivalence vs equality stuff in js.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.