2

I have 2 main components on a react app: one is a table with a bunch of data (using Rsuite Table), the other, above the table, is just a div with multiple input text fields. As simple as this:

input-fields

If, for example, on the 'firstName' input field, the user just types a T letter, the table grid should immediately display only names that include a T in the name. This would be fine, my actual problem, is how to make this happen for the 3 inputs you see in the image above. This is, if the user types in stuff on multiple input text boxes? Take a look below:

Here's how my main App.js looks:

function App() {
  const [dummyData, setDummyData] = useState([]);
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [city, setCity] = useState('');

  const firstNameHandler = (input) => {
    setFirstName(input);
  }
  const lasttNameHandler = (input) => {
    setLastName(input);
  }
  const firstNameHandler = (input) => {
    setCity(input);
  }

  useEffect(() => {
    const generateRandomData = () => {
      setDummyData([...Array(100).keys().map(key => {
        return {
          firstName: faker.name.firstName(),
          lastName: faker.name.lastName(),
          city: faker.address.city()
        }
      })
    )}  
    generateRandomData();
  }, []);

  return(
    <Filters changeFirstName={firstNameHandler} changeLastName={lastNameHandler} changeCity={cityHandler}/>
    <InfoTable items={dummyData}/>
);
  
}
export default App;

PS: The states are being lifted up from the Filterscomponent. I use useEffect to generate random data when the component first loads. And the dummyData, passed into InfoTable is just an array with objects randomly generated using faker.js and they look like this:

{
 firstName: "John",
 lastName: "Smith",
 city: "San Francisco"
}

My Filters.jscomponent looks like this:

function Filters = (props) => {
  const firstNameInputHandler = (event) => {
    props.changeFirstName(event.target.value);
  }
  const lastNameInputHandler = (event) => {
    props.changeLastName(event.target.value);
  }
  const cityInputHandler = (event) => {
    props.changeCity(event.target.value);
  }
  
  return(
    <div className="user_inputs">
      <label htmlFor="firstName">First Name:</label>
      <input id="firstName" type="text" onChange={firstNameInputHandler}/>
    </div>
    <div className="user_inputs">
      <label htmlFor="lastName">Last Name:</label>
      <input id="lastName" type="text" onChange={lastNameInputHandler}/>
    </div>
    <div className="user_inputs">
      <label htmlFor="city">City:</label>
      <input id="city" type="text" onChange={cityInputHandler}/>
    </div>
  );
}
export default Filters;

So here as you can see, I'm lifting the states up, so I have them avaliable on the App.js so I could filter the array that I pass on the table props.

My InfoTable.jscomponent is very straight-foward and looks like this, again, using Rsuite Table:

function InfoTable = (props) => {
  return(
    <Table virtualized height={500} data={props.items} rowHeight={30}>
       <Column width={60}>
         <HeaderCell>First Name</HeaderCell>
         <Cell dataKey="firstName"/>
       </Column>
       <Column width={60}>
         <HeaderCell>Last Name</HeaderCell>
         <Cell dataKey="lastName"/>
       </Column>
       <Column width={60}>
         <HeaderCell>City</HeaderCell>
         <Cell dataKey="city"/>
       </Column>
    </Table>
  );
}
export default InfoTable;

The table displays indeed fine, but what I want is the data to be filtered according to the various text-fields that may or may not be typed in by the user. For example: if the user types Jo on first name input field, and S on the last name input field, I want the table to display only objects that match this criteria. And then if the user types California, I want the table to re-adjust to only people from california with first names that include Jo and last names that include S. And if the user deletes everything, the table should be with the original 100 values.

One way I figured a small part of the issue is, on App.js I check if firstName state length is greater than 0, if so, I run the .filter on the array I pass as props. So inside the return, regarding the grid component, I render it conditionally as suck:

return(
  bla bla bla (...)
  {firstName.length === 0 && <InfoTable items={dummyData} />
  {firstName.length > 0 && <InfoTable items={dummyData.filter((obj) => {
    return obj.firstName.includes(firstName)})
  }
);

I can 'kind of' use this approach to check if there is input or not in a field, and render the table with the 100 rows or filtered by that criteria. The problem is, how do I make this work for the 3 inputs? For example: if the user types Jo on First Name input field, and S on the Last Name input field, I want the table to display only rows that match this criteria (objects with first names that include Jo AND last names that include S). And then if the user types California on the City field, I want the table to re-adjust to only people from California with first names that include Jo and last names that include S. And if the user deletes the text from all 3 inputs, the table should re-ajust to display the original 100 values.

Please help.

TiagoRibeiro
  • 306
  • 6
  • 24

2 Answers2

1

With the example given in mind, the solution is as clean and simple as:

const filterCriteria = (obj) => {
  return obj.firstName.includes(firstName)
  && obj.lastName.includes(lastName)
  && obj.city.includes(city);
}

return(
  <InfoTable items={dummyData.filter(filterCriteria)} />
);

This provides the desired result.

TiagoRibeiro
  • 306
  • 6
  • 24
0

Sorry that I don‘t have enough reputations to comment, so I answer the question

If this is a demo, or data size is small, I think filter is totally ok, just simply chain the filter:

const newData = originalData.filter(condition1).filter(condition2).filter(condition3)

If this is a productive web application, maybe we should let the API from backend does the stuff about filter, because data maybe huge and may rely on some kind of search engines

If you really want to do it only on the client side, and data maybe large too, there should be some kind of technques like search engines only for client side, but I don‘t have experiences about this part

acfasj
  • 76
  • 7
  • The issue here is: I only want to take into consideration the fields the user has typed in, not everyone of them... :( (English is not my native language so sorry if it's not clear some of what I say) – TiagoRibeiro Jun 13 '21 at 18:00
  • Sorry that I didn't get your questions, I am not a native English speaker either. But I still don't understand your really question is, what do you mean `not everyone of them`, maybe you could add a CodeSandbox demo to show us – acfasj Jun 14 '21 at 07:28
  • Like I said in the final part of the post. My table has 100 rows of random generated data and those 3 text input fields. If the user types 'Jo' on First Name input field, and 'S' on the Last Name input field, I want the table to display only objects that match this criteria. And then if the user types California, I want the table to re-adjust to only show people from California with first names that include 'Jo' and last names that include 'S'. And if the user deletes everything from all user input fields, the table should re-adjust to show againthe original 100 values. – TiagoRibeiro Jun 14 '21 at 07:55
  • then just simply filter, what‘s the problem? – acfasj Jun 14 '21 at 08:31
  • If I always filter, the table never displays anything. the input-text field states (firstName, lastName and city) start as empty strings, as usual. And if I filter according to the 3 states like you're suggesting, it will try to find objects where the firstName is an empty string, lastName is an empty string, etc.. :c I want it to only filter if there's something there, and when it's empty strings, it should show all results, and not results with empty strings on the fields – TiagoRibeiro Jun 14 '21 at 08:34
  • `const newData = originalData.filter(item => !value || item.firstName.includes(value))`, why not do some checkings, add a condition to see the value is empty – acfasj Jun 14 '21 at 08:45
  • And I see you question editing history, no need for multiple `InfoTable ` compoenent, just single and one `dummyData ` after filtered is ok, chained the filter as I said@TiagoRibeiro – acfasj Jun 14 '21 at 08:52
  • That does indeed make sense buddy, but if I have instead of 3 filters, if I have 20 filters? Or 30? WIll be problematic xD – TiagoRibeiro Jun 14 '21 at 08:58
  • @TiagoRibeiro If you dont care about the performace, just worry about the code shape, just take all filters as an array , and reduce the array as a signle filter function, just as the redux [`compose`](https://redux.js.org/api/compose) function did – acfasj Jun 14 '21 at 09:12