0

I have a table of about 500 items, all I'm trying to do is push data to the this.state.data and re-render the table. What's happening is that the row is added, but the data is not shown in the row. If I do a this.forceUpdate(), after a short time, then the data magically appears in the row. I'm assuming my re-render is occurring before the state is updated, how do I get around this? Here's the code that's adding to this.state.data and re-rendering:

// the format we expect
scrubData: function(rawData, cb) {
    const scrubbedData = rawData.map((object, idx) => {
        let { mediaserver = [] } = object.Relations;
        let { latitude = [0], longitude = [0] } = object.Properties;

        return {
            label: object.Label || '',
            name: object.Name || '',
            mediaserver: mediaserver[0] || '',
            geo: `${latitude[0]}, ${longitude[0]}`,
            status: '',
            event:  '',
        }
    });

    if (cb) {
        cb(scrubbedData);
        return;
    }

    return scrubbedData;
},

// push one item to the data array, so we don't have to call
// the entire data set all over again
addData: function(rawData) {
    const scrubbedData = this.scrubData([rawData], (scrubbedData) =>  {
        this.state.data.unshift(scrubbedData);

        this.setState({
            data: this.state.data,
        });
    });
},
stevenlacerda
  • 1,187
  • 2
  • 9
  • 21

2 Answers2

0

It's because you are using unshift. With react, you should always mutate state by providing updated state, not by changing the current state.

Instead of using unshift, you can use something like concat, or the spread operator.

Using concat:

this.state.data.concat([scrubbedData]);

this.setState({
    data: this.state.data,
});

Or using the spread operator:

this.setState({
    data: [...this.state.data, scrubbedData]
});

I recommend checking out this stackoverflow post which lists all the mutating methods for arrays.

React works internally when doing the state diff by comparing references to objects/arrays, and since you are mutating the array, the reference is still the same, so React does not detect that a change has been made.

Herein lies the benefit of using an immutable library with React, as changes will always produce a copy, so you can remove an entire class of bugs such as this.

EDIT:

You are calling this.scrubData before adding the new row, and the result is that the new row has none of the additional data that you want appended to it. Try adding the new row to the array first, and then calling that function to append data to each row.

Community
  • 1
  • 1
Wolfie
  • 1,369
  • 8
  • 16
  • Thanks for the input, but same result. It's really weird, it notices that there's a new row, adds the row, but doesn't retrieve the data for that row. – stevenlacerda Aug 07 '17 at 17:38
  • @SteveLacerda you are calling `this.scrubData` with old data, not with the updated data. – Wolfie Aug 07 '17 at 17:50
  • I'm looking at the data, and it looks correct. Here's what I'm looking at: – stevenlacerda Aug 07 '17 at 18:45
  • Sorry, hit enter on accident. Here's the complete: I'm looking at the data, and it looks correct. Here's what I'm looking at: before add: this.state.data.length 255 after add: this.state.data.length 256 Then, with the new state data, I map through TableRows, creating my rows, and the data is there and the object is there, but it renders an empty row. What am I missing??? – stevenlacerda Aug 07 '17 at 18:56
0

Okay, so I finally got this to work. I had to use componentWillUpdate. Not sure if this is correct, but it works now.

export default class Table extends React.Component {
   constructor(props) {
      super(props);

      this.state = {
         tableHeight: "50vh",
         data: [],
      }
   }

   componentWillUpdate(nextProps, nextState) {
      this.state.data = nextProps.data;
   }

   render() {
      const tableRow = this.state.data.map((object, idx) => {
         return (
            <TableRow key={idx} data={object} />
         )
      })

      return (
         <div className="table">
                  <tbody>
                     {tableRow}
                  </tbody>
               </table>
            </div>
         </div>
      )
   }
}
stevenlacerda
  • 1,187
  • 2
  • 9
  • 21