React-bootstrap-table2: Table not updating when formatter is setted

Created on 25 Oct 2018  ·  22Comments  ·  Source: react-bootstrap-table/react-bootstrap-table2

My column setting are these:

columns = {
    dataField: 'inexistentField',
    text: "Obj state",
    formatter: (cell, obj) => {
        console.log('Obj -> ', obj); // Only executed when on table change function is executed by table
    }
}

Table does not update when I call my tableChange function (one that is passed down to onTableChange) externally (not using one of the page buttons).

When I click on any sortable column, content generated by formatter function changes normally. If data changes a lot (for instance, with different number of elements) table works fine. I've debugged with React Dev Tools and my state and table props are correct.

Version bootstrap-table: 1.3.0

Most helpful comment

How could I define dinamyc data on one column? For instance, given a value in one of my obj's field, show a specific icon? Because if formatter can't manage dinamyc data and formatExtraData doesn't receive the current row, I have no idea how we could solve a problem like that one.

Thanks in advance

All 22 comments

@Genarito I think I need more information, if ok, please made a simple and minimal example on https://codesandbox.io . I will help you to check.

But please make sure that you dont use and state data or external data in column.formatter:
https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnformatter-function

I was using It with state data, I didn't read the warning about formatExtraData. Thank you so much and sorry about the inconvenients

How could I define dinamyc data on one column? For instance, given a value in one of my obj's field, show a specific icon? Because if formatter can't manage dinamyc data and formatExtraData doesn't receive the current row, I have no idea how we could solve a problem like that one.

Thanks in advance

I have the same problem. How to deal with this formatter, where cell is type_id

formatCustomerType(cell, row, rowIndex, formatExtraData) {
  return this.state.types[cell] // e.g. cell = 'key1'
}

and types are fetched from API

API.get("/types")
   .then((res) => {
     this.setState({
       types: res // {key1: "val1", key2: "val2"}
     })
   })

Hi @AllenFang , here's an example of what I was trying to do - highlight alerted row using rowClasses and add alert icon using formatter: https://codesandbox.io/s/0xvw06wxpv. Unfortunately just like @Genarito I'm not able to dynamically change column formatting using row's data. I have a workaround using css but I'm not happy with that.

Does the usage of row's data counts as violation of

Don't use any state data or any external data in formatter function

or this is some kind of a bug?

Thank you :)

@jkrawczyk currently, performance is biggest concerns for me. So in current design, please don't use any external data or state. if you have, please pass them via formatExtraData

@jkrawczyk btw, current there's a bug probably about this. I will check this out and get back to you here. thanks

@jkrawczyk What is not documented yet is that the grid needs you to treat the data as immutable, much like redux requires. This way it knows what to redraw, etc. I have modified your example not to directly mutate state and it works as expected. https://github.com/react-bootstrap-table/react-bootstrap-table2/issues/656#issuecomment-443488050

https://codesandbox.io/s/v8pz48rl30

@waynebrantley I believe that in my example the data was immutable thanks to spread operator. Actually in your example it's not working as expected because there is still no [ALERT-ICON] which should appear along with the row highlight - rowClasses works fine but column formatter does not.

@AllenFang Can you look at examples above? I believe that both follow Immutable Update Pattern and were believed to work (according to https://github.com/react-bootstrap-table/react-bootstrap-table2/issues/656#issuecomment-443488150) but apparently something is still not working.

Thank you! :)

@jkrawczyk the spread operator does not duplicate arrays. Your data is mutated. Use splice to duplicate the array and retry - I believe you will find it works properly.

@waynebrantley As I said your example doesn't work correctly either because [ALERT-ICON] string does not show up :) Spread operator in my example creates a copy of an array what is explained here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Copy_an_array

@jkrawczyk the spread operator does copy the array...if the array is simple value. However, If the item being spread into the target is an object, only a reference to that object will be copied. So your data is mutated! (It copied the array...but did not copy the objects...only a reference to the objects)

However, I am still unable to get your example to work. I tired using formatExtraData and a dummy field, it did not work - seemed to be related to the property called 'alert'. Changed the property to another name showAlert in my codesandbox and that causes an error in formatExtraData for some reason.

@waynebrantley You're of course right! My mistake, sorry 🤦‍♂️ The array is indeed new but it holds references to old objects, thank you for pointing that out.

I am having the same issue and i can't get it to work, the formatter function doesn't get called on data change here

Did you guys manage to get it working?

add alert to format extra data on the column since column depends on values other than column value.

formatExtraData: {
        alert
      }

That doesn't really work, what does work however is setting it as a dummy field OR adding the label object to formatExtraData like so:

formatExtraData: { alert: <span className="badge badge-warning ml-2">Alert</span> }

It honestly doesn't make much sense

No, you just list the extra data....exactly like I have it above...just the property name.
Think of it like this - the rendering is cached and does not re-render unless it has to. I knows it has to if the field changes....which in your case does not change...but there are other fields it is dependent on - and you simply list them in the formatExtraData..

Can you please provide a code sandbox? The one you sent above doesn't work, and adding your code throws errors. As far as i know formatExtraData is supposed to receive an object of key: value pairs that get passed on to the formatter function.

Sorry, I was thinking about another ticket.
In your case - the comparison needs to be done on the ROW values - not the column value (because you use other fields in the row). So, if you make it a dummy field - that is exactly what will happen.

https://codesandbox.io/s/vn2y20znw3

I had a problem like these and after hours of logging/trying other things, I added the hack described below. It works and may help someone else, but I wonder if there is a cleaner way?

Created a (react-bootstrap) Modal as a pick list where user includes 1 item from the child table to run an action on the parent entity:

  1. Main (parent) table is react-bootstrap-table-next, BootstrapTable.
  2. Main table implements a BootstrapTable as its child in its expandRow.renderer().
  3. Data is static per modal instance, except column #1 in parent table. That 1 column is set dynamically by an onSelect call from the child table. It contains either an _id from 1 row in the child table, or an empty string.
  4. When column #1 is set with an id, a cell.length check in its formatter renders a green checkmark instead of a grey "X". (Signifies a selection was made.) Before my hack, setting the column 1 data did not render the checkmark.
  5. I know from logging that the data prop to Main table has the right column #1 data and that my modal component is rendering. But from logging the functional component that is the formatter of column #1, I know it did not get called.

Tried usual clones to get BootstrapTable to re-render (also tried this on tableProps.columns[]) e.g.:
tableProps.data = tableProps.data.map(obj => ({…obj}));

The only thing that worked was changing the length of tableProps.columns[]. After spending hours on this, checked here: react-bootstrap-table2/src/row/should-updater.js. So I added this hack to vary columns.length each time the onSelect handler needs to render a changed selection. The hack isn’t expensive, table re-renders and does not jerk. Actually performs perfectly to the eye. But is there a better way?

Here’s the function I use to vary the columns[] config each time:

const touchConfigsToInduceRender = columns => {
  // Using "hidden: true" here did NOT work.
  const dummyCol = {
      dataField: 'induceRender',
      text: '',
      headerStyle: {display:'none'},
      style: {display:'none'},
  };

  if (get(columns, [columns.length - 1, 'dataField']) === dummyCol.dataField) {
      columns.pop();
  } else {
      columns.push(dummyCol);
  }
  return columns;
};

I think codesandboxes of it not working would be helpful to get problems fixed or to see what you might be doing wrong. In addition a 2nd codesandbox that contains your workarounds actually working would be much more useful...

Thanks Wayne. I'll be looking for a non-hack solution. If I find one, I'll share it here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ethannkschneider picture ethannkschneider  ·  3Comments

kamarajuPrathi picture kamarajuPrathi  ·  4Comments

sudravi picture sudravi  ·  3Comments

prajapati-parth picture prajapati-parth  ·  4Comments

josefheld picture josefheld  ·  3Comments