How to filter in reactJS?

1

I have a component that is a <select> that shows all the categories, and I would need to make it work, let's say with a reagent to be able to filter the demos by categories.

This is the select component

import React from 'react';

class Category extends React.Component {

constructor() {
    super();
         this.state={items:[]};
  }
  componentDidMount() {

        fetch('http://localhost:3000/api/categories')
          .then(result => result.json())
          .then(items => {this.setState({items});
        });
}

  render() {
     return ( 
<div>
    <center>
        <select>
            <option default> ---- Select Category ---- </option>

            {this.state.items.length ?
            this.state.items.map(item=>

            <option>{item.name}</option>
            
                ) 
            : <li>Loading...</li>
          }
        </select>
    </center>
</div>
     );
  }
}

export default Category;

And this one of the content to filter

import React from 'react';
var imgdir = '../../../assets/images/';


class Content extends React.Component {

    constructor() {
    super();
         this.state={items:[]};
  }
  componentDidMount(){
    fetch('http://localhost:3000/api/demos')
        .then(result=>result.json())
    .then(items=>this.setState({items}))
  }
  render() {
    return(
        <ul>
          {this.state.items.length ?
            this.state.items.map(item=>

                <div id="1" className="col-lg-3 col-md-4 col-sm-6 col-xs-12">
    <div className="hovereffect">
        <img className="img-responsive" src={imgdir + item.img} alt={item.name} />
            <div className="overlay">
                <h2>{item.name}</h2>
                <p> 
                    <b>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</b>
                </p> 
                <p> 
                    <a href="#">VIEW DEMO</a>
                </p> 
            </div>
    </div>
</div>

                ) 
            : <li>Loading...</li>
          }
      </ul>
   )
  }
}

export default Content;

Each category has an id in its database, and each div as you see has an id that in this case I assigned it manually because I still did not do the part to get that id from the DB but skipping this, I would like to know how to make this filtering work.

PS: the id of the category can be obtained using {item.id}

    
asked by Santiago D'Antuoni 17.01.2017 в 19:51
source

1 answer

1

This question can again be solved using Context as I explained it in your previous question .

The method for filtering can be defined in the parent of the filters and the table. This method can receive the new value selected in the filters component and will be updated in the parent's state. When the state of the parent is updated, the context entry named category will be reflected in all children that have specified that it is available.

In the following case, filtering is done by category id.

class Filters extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      categories: []
    }
  }
  
  componentDidMount () {
    let categories = [
      {
        name: 'All categories',
        id: 0
      },
      {
        name: 'Computadoras',
        id: 1
      },
      {
        name: 'Televisores',
        id: 2
      },
      {
        name: 'Consolas',
        id: 3
      }
    ];
    this.setState({ categories });
  }
  
  render () {
    return (
      <select onChange={this.onChange.bind(this)}>
        {
          this.state.categories.map(c => 
            <option value={c.id}>{c.name}</option>
          )
        }
      </select>
    );
  }
  onChange (e) {
    this.context.applyFilters(parseInt(e.target.value));
  }
}

Filters.contextTypes = {
  applyFilters: React.PropTypes.func
};

class Products extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      products: []
    };
  }
  
  componentDidMount () {
    let products = [
      {
        name: 'TV Samsung Curve',
        price: 1099.90,
        category: 2
      },
      {
        name: 'Play Station 4',
        price: 1899.90,
        category: 3
      },
      {
        name: 'Dell XPS 13',
        price: 1999.90,
        category: 1
      },
      {
        name: 'XBox 360 S',
        price: 769.90,
        category: 3
      }
    ];
    let categories = [
      {
        name: 'Computadoras',
        id: 1
      },
      {
        name: 'Televisores',
        id: 2
      },
      {
        name: 'Consolas',
        id: 3
      }
    ];
    this.setState({ products, categories });
    this.backup = products;
    this.category = 0;
  }
  componentWillUpdate (props, state, ctx) {
    if (this.category !== ctx.category) {
      this.category = ctx.category;

      // 0 = se muestra todo
      if (this.category === 0) {
        this.setState({
          products: this.backup
        });
      }
      else {
        let products = this.backup.filter(p => (
          p.category === this.category
        ));
        this.setState({ products });
      }
    }
  }
  getCategoryName (id) {
    let cat = this.state.categories.filter(c => (
      c.id === id
    ));
    return cat[0].name;
  }
  render () {
    return (
      <table>
        <thead>
          <tr>
            <th>Nombre</th>
            <th>Precio</th>
            <th>Categoría</th>
          </tr>
        </thead>
        <tbody>
          {
            this.state.products.map(p => (
              <tr>
                <td>{p.name}</td>
                <td>{p.price}</td>
                <td>{this.getCategoryName(p.category)}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    )
  }
}

Products.contextTypes = {
  category: React.PropTypes.number
};

class Parent extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      category: 0
    };
  }
  getChildContext () {
    let _this = this;
    return {
      category: this.state.category,
      applyFilters (category) {
        _this.setState({category });
      }
    };
  }
  render () {
    return (
      <div className="data">
        <Filters />
        <Products />
      </div>
    )
  }
}

Parent.childContextTypes = {
  category: React.PropTypes.number,
  applyFilters: React.PropTypes.fn
};

ReactDOM.render(
  <Parent />,
  document.getElementById('app')
);
body {
  padding: 1.5rem;
}
select {
  border: 1px solid #ccc;
  border-radius: 3px;
  padding: .5rem 1rem;
}
table {
  border-collapse: collapse;
  margin-top: 30px;
  width: 100%;
}
table thead {
  border-bottom: 2px solid #ccc;
}
table th, table td {
  padding: .5rem .75rem;
}
table tr:not(:last-of-type) {
  border-bottom: 1px solid #eee;
}
table tbody tr:nth-child(odd) {
  background-color: #f9f9f9;
}
<div id="app"></div>

The code is relatively simple. When a category is chosen in the component Filters is sent to the parent, it updates its status and, at the same time, the context. Children will receive the "notification" that the context has been updated and, in the Products component, this new value is received via hook componentWillMount . This component has a backup of the products that will be used to filter them and, in case "all categories" is selected, the backup is assigned to the state.

In this Pen you can see the example working.

    
answered by 21.01.2017 в 15:02