Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

145
Views
react merging two components and sorting data by date and time (newest at top oldest and bottom)

I have codes below

code removed

The problem is it prints rows of expenses and then rows of incomes (i.e posts from expenses are printed first then after them incomes are printed). I need them to be mixed in and printed accordingly by date and time (newest post goes to top and oldest at bottom)

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

To minimize your code change, I'd propose to add another state called row and use componentDidUpdate to listen to expenses and incomes states change for row state update.

import axios from 'axios';
import React, { Component } from 'react'
import ExpenseRow from './ExpenseRow';
import IncomeRow from './IncomeRow';

class IncomeExpenseSummary extends Component {

    constructor(props) {
        super(props);
        this.state = {
            expenses:[],
            incomes:[],
            rows: []
        }
    }
   
    componentDidUpdate(prevProps, prevState) {
       //trigger when incomes or expenses change
       if(prevState.incomes !== this.state.incomes || prevState.expenses !== this.state.expenses) {
          //add type to recognize `income` in mixed list
          const updatedIncomes = [...this.state.incomes].map(income => ({...income, type: 'income'})) 
          //add type to recognize `expense` in mixed list
          const updatedExpenses = [...this.state.expenses].map(expense => ({...expense, type: 'expense'})) 
          //sort all rows with the mixed list
          const sortedRows = [...updatedExpenses, ...updatedIncomes].sort((a,b) => new Date(b.created_at) - new Date(a.created_at)) 
          this.setState({ rows: sortedRows })
       }
    }

    componentDidMount(){
        this.getExpenseList();
        this.getIncomeList();
    }

    getExpenseList = () =>{
        //if you use an arrow function, you don't need to pass variable for `this`
        axios.get('/get/expenses',{
            params:{_limit: 3}
        }).then((response) => {
            this.setState({
                expenses: response.data
            });
        });
    }

    getIncomeList =() =>{
        //if you use an arrow function, you don't need to pass variable for `this`
        axios.get('/get/incomes',{
            params:{_limit: 3}
        }).then((response) => {
            this.setState({
                incomes: response.data
            });
        });
    }

    render(){
        return(
            <div>
                <table className="table"> 
                {/* <toast/> */}
                <thead>
                    <tr>
                        <th scope="col">Date</th>
                        <th scope="col">Name</th>
                        <th scope="col">Price</th>
                        <th scope="col">Category</th>
                    </tr>
                </thead>
      <tbody>
            {this.state.rows.map((row) => row.type === 'expense' ? <ExpenseRow data={row} key={row.title}/> : <IncomeRow data={row} key={row.title}/>)}
      </tbody>
            </table>
            </div>
    );
    }
  }


export default IncomeExpenseSummary;
about 4 years ago · Juan Pablo Isaza Report

0

If you are aren't worried about rendering duplicate data, this should work for you. I simply merged the two states, sorted them by date, and passed that to your new combined component ExpenseIncomeRow. If you are actually trying to merge the data based on key/value properties, then this solution may not be ideal for you.

import axios from 'axios';
import React, { Component } from 'react'
import ExpenseIncomeRow from './ExpenseIncomeRow';

class IncomeExpenseSummary extends Component {

    constructor(props) {
        super(props);
        this.state = {
            expenses:[],
            incomes:[],
        }
    }

    componentDidMount(){
        this.getExpenseList();
        this.getIncomeList();
    }

    sortByDate = (data) => {
        data.sort((a, b) => new Date(b['created_at']) - new Date(a['created_at']))
    }

    getExpenseList =() =>{
        let self=this;
        axios.get('/get/expenses',{
            params:{_limit: 3}
        }).then(function(response)
            {self.setState({
                expenses: response.data
            });
        });
    }

    getIncomeList =() =>{
        let self=this;
        axios.get('/get/incomes',{
            params:{_limit: 3}
        }).then(function(response)
            {self.setState({
                incomes: response.data
            });
        });
    }


    render(){
        return(
            <div>
                <table className="table"> 
                {/* <toast/> */}
                <thead>
                    <tr>
                        <th scope="col">Date</th>
                        <th scope="col">Name</th>
                        <th scope="col">Price</th>
                        <th scope="col">Category</th>
                    </tr>
                </thead>
                <tbody>
                {this.sortByDate(this.state.expenses.slice(0, 4).concat(this.state.incomes.slice(0, 4)).map(function(x, i){
                    return <ExpenseIncomeRow key={i} data={x} />
                }))}
            </tbody>
            </table>
            </div>
        );
    }

}

export default IncomeExpenseSummary;

Here is the new component:

import React, { Component } from 'react'


class ExpenseIncomeRow extends Component {
    render(){
        return (
            <tr className='alert alert-success'>
                <th>{this.props.data.created_at}</th>
                <td>{this.props.data.title}</td>
                <td>{this.props.data.amount}</td>
                <td>{this.props.data.category}</td>
            </tr>
        )
    }
}
export default ExpenseIncomeRow;

I have changed your imports but you will actually have to set up a new component on your end for it to work properly.

about 4 years ago · Juan Pablo Isaza Report

0

I suggest adding a zip() helper:

// Zip array arguments together.
//
//     zip([1, 2, 3], [4, 5, 6])
//     //=> [[1, 4], [2, 5], [3, 6]]
//     zip([1], [2, 3], [])
//     //=> [[1, 2, undefined], [undefined, 3, undefined]]
//
function zip(...arrays) {
  if (!arrays.length) return [];
  const length = Math.max(...arrays.map(array => array.length));
  return Array.from({ length }, (_, i) => arrays.map(array => array[i]));
}

If you are already using a library with helper functions (Underscore.js, Lodash, Ramda, etc.), chances are that this function is available through it.

You can then use it within your JSX in the following manner:

// moved taking the first 3 elements up, to simplify the JSX
const take = (n, array) => array.slice(0, n + 1);
const expenses = take(3, this.state.expenses);
const incomes = take(3, this.state.incomes);

// ...

<tbody>
  {zip(expenses, incomes).map(([expense, income], i) => (
    <>
      <ExpenseRow key={i} data={expense} />
      <IncomeRow key={i} data={income} />
    </>
  ))}
</tbody>

This solution assumes the HTTP request returns the items in the correct order. If this isn't the case, you'll need to sort both the arrays first.


If the length of expenses and incomes can be different. Say expenses has length 1 and incomes has length 3. Then you'll probably want to add an additional check for both expenses and incomes (example only shows expenses).

{expense && <ExpenseRow key={i} data={expense} />}

Or you could return null in ExpenseRow if no data is provided.

class ExpenseRow extends Component {
    render() {
        if (!this.props.data) return null;
        // ...
    }
}
about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!