user99999
user99999

Reputation: 2022

Sorting columns using asc/desc and default ordering

I have multiple columns in my table, for example:

id | name | amount | description

And I want to sort each column - on the first click in ascending order, on the second on descending order, on the third go back to default, and all over again.

The default is the id column sorded in asc order.

So, the default state in the reducer is:

sort: {
    key: 'id',
    desc: false
}

The next steps on clicking name column would be:

sort: {
    key: 'name',
    desc: false
}

sort: {
    key: 'name',
    desc: true
}

sort: {
    key: 'id',
    desc: false
}

The view calls an action using column's name as a parameter:

<td onClick={() => this.props.sort('name')}>Name</td>
<td onClick={() => this.props.sort('amount')}>Amount</td>

An action should dispatch such key and desc values so that it matches my pattern:

export function sort(key) {
    return dispatch => {

    };
};

How can I do this?

Upvotes: 0

Views: 3587

Answers (1)

Andreyco
Andreyco

Reputation: 22862

Here you go, brief explanation in code sample. I setup 2 columns only, cause I am lazy, sorry.

Fiddle: https://jsfiddle.net/u1wru0gb/1/

const data = [
    { id: 1, name: 'Bruce' },
    { id: 3, name: 'Martin' },
    { id: 2, name: 'Andrew' },
];

/**
 * Nothing interesting, just render...
 */
function Table({ data, sortByKey }) { 
    const renderRow = ({ id, name }, idx) => (
        <tr key={idx}>
            <td>{id}</td>
            <td>{name}</td>
        </tr>
    )

    return (
        <table>
            <tr>
                <th onClick={sortByKey('id')}>ID</th>
                <th onClick={sortByKey('name')}>Name</th>
            </tr>
            { data.map(renderRow) }
        </table>
    );
}

class Container extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            sort: {
                key: undefined,
                // 0 - not ordering
                // 1 - asc
                // 2 - desc
                order: 0,
            },
        };

        this.sortByKey = this.sortByKey.bind(this);
    }

    sortedData() {
        const { key, order } = this.state.sort;

        // Only sort if key is provided & order != 0.
        if (key && order) {
            // Comparison function for "asc" sorting.
            function compare(a, b) {
                if (a[key] < b[key]) return -1;
                if (a[key] > b[key]) return 1;
                return 0;
            }

            // Attention! Sort mutates array, clone first.
            return [...this.props.data].sort((a, b) => {
                // Interesting part. Sort in "asc" order. Flip if want "desc" order!
                return compare(a, b) * (order === 1 ? 1 : -1);
            });
        }

        // Return original data (order = 0)
        return this.props.data;
    }

    sortByKey(key) {
        return () => {
            const sort = (this.state.sort.key === key)
                // Key matches, update order
                ? { key, order: (this.state.sort.order + 1) % 3 }
                // Key differs, start with "asc" order
                : { key, order: 1 };
            this.setState({ sort });
        }
    }

    render() {
        return (
            <Table data={this.sortedData()} sortByKey={this.sortByKey} />
        );
    }
}

ReactDOM.render(
    <Container data={data} />,
    document.getElementById('container')
);

Upvotes: 1

Related Questions