0

I have data coming from an external API that I am trying to display in a table. The data comes back with date time stamps.

I'd like to render the data in a table, but with an extra row in that displays the date when the date changes.

What I've tried

  • I've attempted updating a lastDate prop value.
  • I've attempted conditional rendering with the (condition) ? expected : other syntax and a variable in the render() method.
  • I can't figure out how to keep track of the current date variable and update it on every loop iteration.
  • I've tried splitting the DateRow and the DataRow into seperate components and passing the date, but the state is not persisted.
  • I'm not sure what is best practice for doing this. Or if it's just a bad idea.

import React from 'react';
import { Link } from 'react-router-dom';
import { Table } from 'react-bootstrap';

import * as helpers from '../../common/helpers';

export default class SomeComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null,
    };
  }

  componentDidMount() {
    fetch(`https://api.example.com`)
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            data: result
          });
        }
      )
  }

  render() {
    const { data } = this.state;
    return (
      <>
        <h4>Most Recent</h4>
        <Table>
          <thead>
            <tr>
              <th>Time</th>
              <th>Column 1</th>
              <th>Column 2</th>
            </tr>
          </thead>
          <tbody>
            {data.map(newRow => (

              // Here I'd like to conditionally render a row if there has been a change in day from the last row before rendering the next row.
              // ie. if previousDate != currentDate
              // <tr>
              //   <td>{helpers.getFormattedDate(newRow.time)}</td>
              //   <td></td>
              //   <td></td>
              // </tr>
              // Then render the following :

              <tr key={newRow.id}>

                <td>
                  {helpers.getFormattedTime(newRow.time)}
                </td>

                <td>
                  <Link to={`/something`}>{helpers.getFormattedNumber(newRow.value)}</Link>
                </td>

                <td>
                  {/* Some more content here */}
                </td>

              </tr>
            ))}
          </tbody>
        </Table>

      </>
    );
  }
}

Any help here would be appreciated, I'm more than a bit lost on this one.

Thanks!

2 Answers 2

1

You can do conditional rendering in React by combining two features:

  1. JavaScript stops evaluating expressions with boolean operators like && and || as soon as the value of the expression can be determined. For example, in func1() && func2(), the function func2 is never called if func1 returns a falsey (null, undefined, false, 0) value. Similarly, in func1() || func2(), func2 is never called if func1 returns a truthy value.

  2. React does not render falsey (except 0) values.

So a common way to do conditional rendering in React is to do something like:

<div>
    { isTrue && (
        <MyConditionalComponent/>
    )}
</div>

Where MyConditionalComponent will only be rendered if isTrue is truthy.

In your case, since you're using a map, you'll probably also want to use the Fragment component which basically allows you to return more than a single component for an element of the map. Alternatively you could use a reduce, but for simplicity I'll just show an example with the map:

<tbody>
  {data.map((newRow, i) => (

    <React.Fragment>
      (i !== 0 && (getDate(newRow.time) !== getDate(data[i - 1].time))) && (
        <tr>
          <td>{helpers.getFormattedDate(newRow.time)}</td>
          <td></td>
          <td></td>
        </tr>
      )

      <tr key={newRow.id}>

        <td>
          {helpers.getFormattedTime(newRow.time)}
        </td>

        <td>
          <Link to={`/something`}>{helpers.getFormattedNumber(newRow.value)}</Link>
        </td>

        <td>
          {/* Some more content here */}
        </td>

      </tr>
    </React.Fragment>
  ))}
</tbody>

Where getDate would be some function that returns just the date part from a Date object.

Note we also make use of the second argument (i) of the map callback, which is the index of the current element to check on the date of the previous element.

Sign up to request clarification or add additional context in comments.

Comments

1

You can use .map's 2nd argument, the index:

{data.map((newRow, index) => {
  const prevRow = data[index - 1];
  return (
    // Fragment is necessary because you will
    // now potentially render 2 elements
    <React.Fragment key={newRow.id}> 
      {prevRow && newRow.date !== prevRow.date && (
        <tr>
          <td>{helpers.getFormattedDate(newRow.time)}</td>
          <td></td>
          <td></td>
        </tr>
      )} 
      <tr>
        // The rest of your component
      </tr>
    <React.Fragment>
  )
}}

Comments

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.