PEPEGA
PEPEGA

Reputation: 2283

Filter state in React without removing data

I'm trying to make a react component that can filter a list based on value chosen from a drop-down box. Since the setState removes all data from the array I can only filter once. How can I filter data and still keep the original state? I want to be able to do more then one search.

Array list:

  state = {
tree: [
  {
    id: '1',
    fileType: 'Document',
    files: [
        {
          name: 'test1',
          size: '64kb'
        },
        {
          name: 'test2',
          size: '94kb'
        }
    ]
  }, ..... and so on

I have 2 ways that I'm able to filter the component once with:

  filterDoc = (selectedType) => {
   //way #1
   this.setState({ tree: this.state.tree.filter(item => item.fileType === selectedType) })

  //way#2
  const myItems = this.state.tree;
  const newArray = myItems.filter(item => item.fileType === selectedType)
  this.setState({
    tree: newArray
  })      
}

Search component:

class SearchBar extends Component {

  change = (e) => {
    this.props.filterTree(e.target.value);
  }

  render() {
      return (
        <div className="col-sm-12" style={style}>
            <input
            className="col-sm-8"
            type="text"
            placeholder="Search..."
            style={inputs}
            />
            <select
            className="col-sm-4"
            style={inputs}
            onChange={this.change}
            >
                <option value="All">All</option>
              {this.props.docTypes.map((type) =>
                <option
                  value={type.fileType}
                  key={type.fileType}>{type.fileType}
                </option>)}
            </select>
        </div>
      )
  }
}

And some images just to get a visual on the problem. Before filter: enter image description here

After filter, everything that didn't match was removed from the state:

enter image description here

Upvotes: 4

Views: 8977

Answers (2)

mikabytes
mikabytes

Reputation: 2165

Do not replace original data

Instead, change what filter is used and do the filtering in the render() function.

In the example below, the original data (called data) is never changed. Only the filter used is changed.

const data = [
  {
    id: 1,
    text: 'one',
  },
  {
    id: 2,
    text: 'two',
  },
  {
    id: 3,
    text: 'three',
  },
]

class Example extends React.Component {
  constructor() {
    super()
    this.state = {
      filter: null,
    }
  }

  render() {
    const filter = this.state.filter
    const dataToShow = filter
      ? data.filter(d => d.id === filter)
      : data

    return (
      <div>
        {dataToShow.map(d => <span key={d.id}> {d.text}, </span>)}
        <button
          onClick={() =>
            this.setState({
              filter: 2,
            })
          }
        >
          {' '}
          Filter{' '}
        </button>
      </div>
    )
  }
}

ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<body>
  <div id='root' />
</body>

Upvotes: 4

jmargolisvt
jmargolisvt

Reputation: 6088

Don't mutate local state to reflect the current state of the filter. That state should reflect the complete available list, which should only change when the list of options changes. Use your filtered array strictly for the view. Something like this should be all you need to change what's presented to the user.

change = (e) => {
   return this.state.tree.filter(item => item.fileType === e.target.value)
}

Upvotes: 2

Related Questions