Fluentui: Values disappear if ComboBox is in DetailList

Created on 19 Dec 2018  路  11Comments  路  Source: microsoft/fluentui

Environment Information

  • __Package version(s)__:
$ cat package.json
{
  "name": "flowerbid",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://kirilligum.gitlab.io/flowerbid/",
  "dependencies": {
    "@uifabric/styling": "^6.38.0",
    "firebase": "^5.6.0",
    "firebase-tools": "^6.1.2",
    "grommet-css": "^1.6.0",
    "mermaid": "^8.0.0-rc.8",
    "node-sass": "^4.11.0",
    "npm": "^6.4.1",
    "office-ui-fabric-react": "^6.109.0",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-router-dom": "^4.3.1"
  },
  "devDependencies": {
    "react-scripts": "^2.1.1"
  },
  "scripts": {
    "start": "set PORT=3006 && react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

  • __Browser and OS versions__: (fill this out if relevant)
    Chrome Version 70.0.3538.110 (Official Build) (64-bit)

Please provide a reproduction of the bug in a codepen:



import React from 'react';

import { DetailsList, SelectionMode} from 'office-ui-fabric-react/lib-commonjs/DetailsList';
import { ComboBox } from 'office-ui-fabric-react/lib-commonjs/ComboBox';

export default class Tryselect extends React.Component {
    state = {
        items : [ {value: 'one', data:['a','b']}, {value: 'two', data:['c','d']} ],
    }

    render(){
        return (
            <DetailsList
                    items={this.state.items}
                    selectionMode={SelectionMode.none}
                    onRenderItemColumn={this._renderItemColumn}
            />
            )
    }
    _renderItemColumn = (item, index, column) => {
        const fieldContent = item[column.fieldName || ''];

        switch (column.fieldName) {
            case 'data':
                return <ComboBox 
          allowFreeform={false}
          autoComplete="on"
                    defaultSelectedKey={item.data[0]} 
                    options={
                    item.data.map(x=>{return {key:x,text:x}})
                }/>;

            default:
                return <span>{fieldContent}</span>;
        }
    }
}

1) click on the combo box in row 1
2) select 'b'
3) click on the combo box in row 2
4) select 'd

Actual behavior:

i) at (3) the value in the row 1 combo box disappears
ii) at (4) the value in the row 1 combo box is absent

Expected behavior:

i) see b in row 1 combo box
ii) see b in row 1 combo box

Priorities and help requested:

Are you willing to submit a PR to fix? Yes

Requested priority: Blocking

Products/sites affected: (if applicable)

ComboBox DetailsList

All 11 comments

Looked at this a bit and I think the issue is with the way the ComboBox options are being created. On each render, the code is creating a new array of options, and the ComboBox is designed to reset the currently selected item when it gets a new array of options.

You can fix this by creating the arrays of options ahead of time and saving them somewhere--in this codepen, I put them in state, but you could put them wherever makes the most sense in your real code. https://codepen.io/ecraig12345/pen/VqmaKL

Let me know if this helps.

(Next time, if you could put your code in a codepen that would be super helpful--there's a template at http://aka.ms/fabricpen.)

@ecraig12345, its _generally_ a good practice to avoid initializing non-primitives in Component render calls, correct? I ask because of some reading I've done recently while analyzing DetailsList perf.

@KevinTCoughlin fieldContent initialization is from the DetailList documentation

@ecraig12345

I'm actually passing the table data and the choices as a prop or,actually, {...this.state}

When I modify the code after two selections (select first row, then select second row checkboxes), the selection disappear.

https://codepen.io/kirilligum/pen/wRogLV

const { DetailsList, SelectionMode, ComboBox } = window.Fabric;

class Tryselect extends React.Component {  

  render(){
    return (
      <DetailsList
        items={this.props.tableItems}
        selectionMode={SelectionMode.none}
        onRenderItemColumn={this._renderItemColumn}
      />
    )
  }

  _renderItemColumn = (item, index, column) => {
    const fieldContent = item[column.fieldName || ''];

    switch (column.fieldName) {
      case 'data':
        console.log('hi',this.props.items.map(x => ({ key: x, text: x })))
        return <ComboBox 
          allowFreeform={false}
          autoComplete="on"
          defaultSelectedKey={this.props.items.map(x => ({ key: x, text: x }))[1].key} 
          options={this.props['items'].map(x => ({ key: x, text: x }))}
        />;

      default:
        return <span>{fieldContent}</span>;
    }
  }
}

class Parent extends React.Component {
  state = {
    tableItems: [ {value: 'one', data:['a','b']}, {value: 'two', data:['c','d']} ],
    items:['a','b']
  }
  render (){
    return   <Tryselect {...this.state}/>
  }
}

ReactDOM.render( 
  <Parent/>,
  document.getElementById('content')
);

@KevinTCoughlin fieldContent initialization is from the DetailList documentation

Hence my question. Currently, DetailsList and its components' render code initializes non-primitives _a ton_. Its why I came across this recommendation on the net. We hope to fix them going forward if it is the recommended way. Anyway unrelated to the issue at hand _I guess_.

It often leads to issues with virtualization and inner component state.

@KevinTCoughlin You're probably right about not initializing non-primitives inline, due to perf and bugs like this. We should really try and keep anti-patterns out of our documentation, so I'll work on updating the DetailsLIst documentation to initialize things ahead of time.

i remove initialization const fieldContent = item[column.fieldName || ''];
the combobox still goes blank

@kirilligum The problem is this line, not the fieldContent one: options={this.props['items'].map(x => ({ key: x, text: x }))}

If you create the options from the items only once and save them somewhere (private member, state, whatever) like in the codepen I linked earlier, that should prevent the options from disappearing.

ok. sounds good. thank you

Just updated my codepen to be closer the new code you shared. https://codepen.io/ecraig12345/pen/VqmaKL?editors=0010

@ecraig12345 , you are amazing. thank you!

Was this page helpful?
0 / 5 - 0 ratings