Valborg
Valborg

Reputation: 105

Create a dynamic onClick function in React

I created a function to sort a state array of objects according to one of the key: value pairs. Here's what it looks like: It's currently working.

State:

class Employee extends Component {
state = {
  employeeData: [],
  filteredEmployeeData: [],
  search: '',
  sort: 'asc',
  error: '',
}

Working function:

  sortFirstName = () => {
    console.log('SORTING!!!!')
    const {employeeData} = this.state
    const sorted = employeeData.sort((a, b) => {
          if (a.name.first < b.name.first){
            return -1
        } else if (a.name.first > b.name.first){
            return 1
        } else {
          return 0
        }
    })
    console.log('Sorted List: ', sorted)
    this.setState({filteredEmployeeData: sorted})
}

Render:

  render () {
    return (
      <div>
        <Container style={{ minHeight: "80%" }}>
        <br/>
          <h1 className="text-center">Search For An Employee</h1>
          <Alert
            type="danger"
            style={{ opacity: this.state.error ? 1 : 0, marginBottom: 10 }}
          >
            {this.state.error}
          </Alert>
          <SearchForm
            handleInputChange={this.handleInputChange}
            search={this.state.search}
          />
        </Container>
        <Container>
        <table className="table table-hover">
        <thead>
            <tr>
            <th scope="col" >Image</th>
            <th scope="col" value="firstName" onClick={(this.sortFirstName)}>First Name</th>
            <th scope="col" value="lastName">Last Lame</th>
            <th scope="col" value="email">Email</th>
            <th scope="col" value="phoneNumber">Phone Number</th>
            <th scope="col" value="city">City</th>
            <th scope="col" value="SSN">SSN</th>
            </tr>
        </thead>
        <tbody>
        {this.state.filteredEmployeeData.map(ee => (
          <EmployeeData
            id={ee.login.uuid}
            key={ee.login.uuid}
            img={ee.picture.thumbnail}
            firstName={ee.name.first}
            lastName={ee.name.last}
            email={ee.email}
            phone={ee.cell}
            city={ee.location.city}
            ssn={ee.id.value}
          />
        ))}
        </tbody>
        </table>
        </Container>
      </div>
    );
  }}

What I'd like to do is update the function to be dynamic so that any of my table headers can be clicked and the data will sort accordingly. I tried it as something like this but I can't quite figure out a way to do it.

My attempt at creating a dynamic sort function (doesn't work):

  sort = (header) => {
    console.log('SORTING!!!!')
    const {employeeData} = this.state
    const sorted = employeeData.sort((a, b) => {
          if (a.header < b.header){
            return -1
        } else if (a.header > b.header){
            return 1
        } else {
          return 0
        }
    })
    console.log('Sorted List: ', sorted)
    this.setState({filteredEmployeeData: sorted})
}

Render with dynamic sorts (doesn't work)

        <thead>
            <tr>
            <th scope="col" >Image</th>
            <th scope="col" value="firstName" onClick={(this.sort('name.first')}>First Name</th>
            <th scope="col" value="lastName" onClick={(this.sort('name.last')}>Last Lame</th>
            <th scope="col" value="email" onClick={(this.sort('email')}>Email</th>
            <th scope="col" value="phoneNumber" onClick={(this.sort('cell')}>Phone Number</th>
            <th scope="col" value="city" onClick={(this.sort('city')}>City</th>
            <th scope="col" value="SSN" onClick={(this.sort('ssn')}>SSN</th>
            </tr>
        </thead>

Any help here would be appreciated.

Upvotes: 2

Views: 76

Answers (2)

Vo Quoc Thang
Vo Quoc Thang

Reputation: 1396

For "name.first" and "name.last", there is a little bit of work. You need to make it into a single word to access it. For nested object I prefer this solution from this post:

function fetchFromObject(obj, prop) {

    if(typeof obj === 'undefined') {
        return false;
    }

    var _index = prop.indexOf('.')
    if(_index > -1) {
        return fetchFromObject(obj[prop.substring(0, _index)], prop.substr(_index + 1));
    }

    return obj[prop];
}
sort = (header) => {
    console.log('SORTING!!!!')
    const {employeeData} = this.state
    const sorted = employeeData.sort((a, b) => {
          const x = fetchFromObject(a, header);
          const y = fetchFromObject(b, header); 
          if (x < y){
            return -1
        } else if (x > y){
            return 1
        } else {
          return 0
        }
    })
    console.log('Sorted List: ', sorted)
    this.setState({filteredEmployeeData: sorted})
}

Upvotes: 1

kunal panchal
kunal panchal

Reputation: 798

As per my understanding you want something like this.

This is dynamic sorting with the string dot notation accessing the object

sort = (header) => {
         const {employeeData} = this.state;
       
    const sorted = employeeData.sort((a, b) => {
      a = getDescendantProp(a, header);
      b = getDescendantProp(b, header);
          if (a < b){
            return -1
        } else if (a> b){
            return 1
        } else {
          return 0
        }
    })
    console.log('Sorted List: ', sorted)
    this.setState({filteredEmployeeData: sorted})
}



function getDescendantProp(obj, desc) {
    var arr = desc.split(".");
    while(arr.length && (obj = obj[arr.shift()]));
    return obj;
}

Upvotes: 0

Related Questions