Render List Of Elements Based On User's Input
Context I'm new to React and so I'm building basic form where I would like to display a list of countries based on the user's input. My app is made up of two components: Filter (this is the input field where the user will enter their input) and Display (this will display the countries that meet the user's input
So far, I've been able to log the user's input to the console. However, inside the useEffect() hook, when I add in a line to update the countries state array to the user's input, nothing happens. How can I update the countries array with the user's input?
CodeApp.js
import React, {useEffect, useState} from 'react'
import axios from 'axios'
import Filter from './components/Filter'
import Display from './components/Display'
function App() {
const [countries, setCountries] = useState(['france', 'spain', 'germany', 'greece', 'portugal'])
const [searchTerms, setSearchTerms] = useState('')
const handleSearchInput = (event) => setSearchTerms(event.target.value)
useEffect(() => {
let results = countries.filter(country => country === searchTerms)
console.log("results: ", results)
setSearchTerms(searchTerms)
setCountries(results)
}, [])
return (
<div>
<h1>Countries</h1>
<Filter value={searchTerms} handleChange={handleSearchInput}/>
<Display filteredArr={countries}/>
</div>
);
}
export default App;
Display
import React from 'react'
export default function Display({filteredArr, arr}){
return(
<p>
{filteredArr.map(country => <div>{country}</div>)}
</p>
)
}
Filter component
export default function Filter({searchResult, handleChange}){
return(
<div>
<input value={searchResult} onChange={handleChange} placeholder='Search for countries'/>
</div>
)
}
Answer
Since the effect hook has an empty dependency array, it runs only on mount - and on mount, the searchTerms
string is empty, so the results
array will also be empty.
Don't use the effect hook - instead, have the handleSearchInput
filter and set state. You'll also need a persistent list of countries - if you filter and set the state of the countries, and the user input changes again, you'll want to subsequently filter the original list of countries, not the already-filtered list of countries.
function App() {
const initialCountries = ['france', 'spain', 'germany', 'greece', 'portugal'];
const [countries, setCountries] = React.useState(initialCountries)
const [searchTerms, setSearchTerms] = React.useState('')
const handleSearchInput = (event) => {
setSearchTerms(event.target.value);
setCountries(
initialCountries.filter(
country => country === event.target.value
)
);
};
return (
<div>
<h1>Countries</h1>
<Filter value={searchTerms} handleChange={handleSearchInput}/>
<Display filteredArr={countries}/>
</div>
);
}
function Display({filteredArr, arr}){
return(
<p>
{filteredArr.map(country => <div>{country}</div>)}
</p>
)
}
function Filter({searchResult, handleChange}){
return(
<div>
<input value={searchResult} onChange={handleChange} placeholder='Search for countries'/>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div class='react'></div>
Your current logic seems a bit odd though - do you really want to only show countries that exactly match the input? I'm not sure what you're trying to achieve here, but if you only want exact matches, it might be more user-friendly to make the countries clickable to toggle them, rather than using a text input. If you want to show multiple countries that contain the same user input substring, use .includes
instead of ===
in the filter test.
You also might want to display all countries again if the input is empty, in which case you'd do:
setCountries(
event.target.value === '' ? initialCountries : initialCountries.filter(
country => country === event.target.value
)
);
Related Questions
- → Import statement and Babel
- → should I choose reactjs+f7 or f7+vue.js?
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → .tsx webpack compile fails: Unexpected token <
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → React Native with visual studio 2015 IDE
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM
- → How do I determine if a new ReactJS session and/or Browser session has started?
- → Alt @decorators in React-Native
- → How to dynamically add class to parent div of focused input field?