0

I am trying to develop a very simple dashboard with some information. I'm trying to add in a search filter into my code so that I can narrow my data by names. In many other tutorials, I found that they have common used name.toLowerCase().indexOf(this.state.filterName.toLowerCase()) >= 0 but I just didn't work for me and would like to seek guidance from you guys.

You may also provide feedback on the general structure of my code!

Tables.js

class Table extends Component {
  constructor(props){
    super(props);

    this.state = {
      filterName: this.props.filterName,
      toShow: 'all',
      myArrays: [
        { 'id': 1, 'name': 'Eric', 'email': 'name1@email.com', 'role': 'student', 'isadmin': 'false' },
        { 'id': 2, 'name': 'Amanda', 'email': 'name2@email.com', 'role': 'student', 'isadmin': 'false' },
        { 'id': 3, 'name': 'Brenda', 'email': 'name3@email.com', 'role': 'staff', 'isadmin': 'true' },
        { 'id': 4, 'name': 'Charles', 'email': 'name4@email.com', 'role': 'teacher', 'isadmin': 'true' },
        { 'id': 5, 'name': 'Daimon', 'email': 'name5@email.com', 'role': 'assistant', 'isadmin': 'false' }
      ]
    };
    this.toShowAdmin = this.toShowAdmin.bind(this);
  }

  toShowAdmin(){
    if (this.state.toShow === 'all'){
      this.setState({ toShow: 'admin' }, () => console.log(this.state.toShow ))
    } else {
      this.setState({ toShow: 'all' }, () => console.log(this.state.toShow ))
    }
  }

  render(){
    let myArrays = []

    if (this.state.toShow === 'all'){
      myArrays = this.state.myArrays;
    } else if (this.state.toShow === 'admin'){
      myArrays = this.state.myArrays.filter(row => row.isadmin === 'true')
    }

    myArrays = this.state.myArrays.filter(row => {
      return row.name.toLowerCase().indexOf(this.state.filterName.toLowerCase()) >= 0;
    });

    return(
      <div>
        <table className="table">
          <thead className="thead-dark">
            <tr>
              <th scope="col"> name </th>
              <th scope="col"> email </th>
              <th scope="col"> role </th>
              <th scope="col"> isadmin </th>
            </tr>
          </thead>

          <tbody>
            {myArrays.map(row=>
              <tr key={row.id}>
                <td> {row.name} </td>
                <td> {row.email} </td>
                <td> {row.role} </td>
                <td> {row.isadmin} </td>
              </tr>
            )}
          </tbody>
        </table>

        <button type='button' className='btn btn-primary' onClick={this.toShowAdmin}> Admins </button>
        { this.state.filterName }
      </div>
    );
  }
}

export default Table;

Main.js

class Main extends Component {
  constructor(props){
    super(props);

    this.state = {
      filterName: ''
    };
    this.filterUpdate = this.filterUpdate.bind(this);
  }

  filterUpdate(value){
    this.setState({ filterName: value}, () => console.log(this.state.filterName))
  }

  render(){
    return(
      <div>
        <Search
          filterName={this.state.filterName}
          filterUpdate={this.filterUpdate}/>
        <Tables
          filterName={this.state.filterName}/>
      </div>
    );
  }
}

export default Main;
MongChangHsi
  • 103
  • 1
  • 12
  • can you add your search component – Learner May 17 '20 at 13:28
  • Do not copy **props to state** as It is **anti-pattern**: [this](https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti-pattern-unconditionally-copying-props-to-state), [this](https://stackoverflow.com/q/27991366/2873538). If you **must** copy, do it after [deep cloning](https://stackoverflow.com/q/122102/2873538). – Ajeet Shah May 17 '20 at 16:44

2 Answers2

1

You have to use componentWillReceiveProps to update your child state with parent props once the child component has been loaded. Add something like this to child component -

componentWillReceiveProps(){
 this.setState({filterName: this.props.filterName})
}

See this Stack Over Flow Answer

Otherwise, if your logic allows you to use the prop directly in render, you may also do -

myArrays = this.state.myArrays.filter(row => {
      return row.name.toLowerCase().indexOf(this.props.filterName.toLowerCase()) >= 0;
 });

edit - use componentDidUpdate()

As rightly pointed out in comments, componentWillReceiveProps() is deprecated. Use componentDidUpdate() instead.

componentDidUpdate(){
     this.setState({filterName: this.props.filterName})
}
Mukesh Keshu
  • 467
  • 2
  • 13
  • 1
    This may work but please note that [`componentWillReceiveProps`](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops) has been deprecated, use [`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) instead. – Ajeet Shah May 17 '20 at 14:36
0

You should use this.props.filterName directly like this:

    myArrays = this.state.myArrays.filter(row => {
      return row.name.toLowerCase().indexOf(this.props.filterName.toLowerCase()) >= 0;
    });
Taghi Khavari
  • 6,272
  • 3
  • 15
  • 32
  • Thank you! It works, may I know why I shouldn't put the props into my state? – MongChangHsi May 17 '20 at 14:36
  • glad it was helpful, That's because you were using ```this.props.filterName``` in ```constructor``` method to set the state and ```constructor``` method will be called only on initialization of ```component``` and after that changes in ```props``` won't change the ```state``` and you're using a cached version of ```this.props.filterName``` – Taghi Khavari May 17 '20 at 16:43