I have a question regarding a React filter search input.
I have two Components the the moment.
FirstPageComponent.js
import React from "react"
import AutoComplete from "./AutoComplete"
export class FirstPageComponent extends React.Component {
constructor() {
super()
this.state = {
rows: [],
loading: true,
}
}
async ponentDidMount(searchTerm = "marvel") {
await fetch(
"=&query=" +
searchTerm
)
.then(response => response.json())
.then(responseData => {
this.setState({
rows: responseData.results,
loading: false,
})
})
.catch(error => {
console.log("Error fetching and parsing data", error)
})
}
render() {
console.log(this.state.rows)
return <AutoComplete movies={["hej"]} />
}
}
and
AutoComplete.js
import React from "react"
export default class AutoComplete extends React.Component {
constructor(props) {
super(props)
this.state = {
rows: [],
loading: false,
userInput: "",
}
}
onChange = e => {
const { movies } = this.props
const userInput = e.currentTarget.value
const rows = movies.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
)
this.setState({
rows,
loading: true,
userInput: e.currentTarget.value,
})
}
onClick = e => {
this.setState({
rows: [],
loading: false,
userInput: e.currentTarget.innerText,
})
}
render() {
const {
onChange,
onClick,
state: { rows, loading, userInput },
} = this
let moviesListComponent
if (loading && userInput) {
if (rows.length) {
moviesListComponent = (
<ul>
{rows.map((movie, index) => {
return (
<li key={index} onClick={onClick}>
{movie}
</li>
)
})}
</ul>
)
} else {
moviesListComponent = (
<>
<p>Couldn't find any movies. Try searching for another movie.</p>
</>
)
}
}
return (
<React.Fragment>
<input type="search" onChange={onChange} value={userInput} />
{moviesListComponent}
</React.Fragment>
)
}
}
I basically want to know if i’m approaching this the right way. I want to make a dynamic request for movie titles from fetch(””) API.
Send down the movie titles as props and then filter the values in autoplete ponent if the user input is similar to the movie title props.
Am i thinking about this the right way? I've tried to have the fetch call in the same ponent as AutoComplete but haven't gotten it to work as i want it to. How would you setup the ponent structure if you'd be solving this issue for example?
Feel free to ask if there’re any confusions.
Thanks
I have a question regarding a React filter search input.
I have two Components the the moment.
FirstPageComponent.js
import React from "react"
import AutoComplete from "./AutoComplete"
export class FirstPageComponent extends React.Component {
constructor() {
super()
this.state = {
rows: [],
loading: true,
}
}
async ponentDidMount(searchTerm = "marvel") {
await fetch(
"https://api.themoviedb/3/search/movie?api_key=&query=" +
searchTerm
)
.then(response => response.json())
.then(responseData => {
this.setState({
rows: responseData.results,
loading: false,
})
})
.catch(error => {
console.log("Error fetching and parsing data", error)
})
}
render() {
console.log(this.state.rows)
return <AutoComplete movies={["hej"]} />
}
}
and
AutoComplete.js
import React from "react"
export default class AutoComplete extends React.Component {
constructor(props) {
super(props)
this.state = {
rows: [],
loading: false,
userInput: "",
}
}
onChange = e => {
const { movies } = this.props
const userInput = e.currentTarget.value
const rows = movies.filter(
suggestion =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
)
this.setState({
rows,
loading: true,
userInput: e.currentTarget.value,
})
}
onClick = e => {
this.setState({
rows: [],
loading: false,
userInput: e.currentTarget.innerText,
})
}
render() {
const {
onChange,
onClick,
state: { rows, loading, userInput },
} = this
let moviesListComponent
if (loading && userInput) {
if (rows.length) {
moviesListComponent = (
<ul>
{rows.map((movie, index) => {
return (
<li key={index} onClick={onClick}>
{movie}
</li>
)
})}
</ul>
)
} else {
moviesListComponent = (
<>
<p>Couldn't find any movies. Try searching for another movie.</p>
</>
)
}
}
return (
<React.Fragment>
<input type="search" onChange={onChange} value={userInput} />
{moviesListComponent}
</React.Fragment>
)
}
}
I basically want to know if i’m approaching this the right way. I want to make a dynamic request for movie titles from fetch(”https://themovie.db”) API.
Send down the movie titles as props and then filter the values in autoplete ponent if the user input is similar to the movie title props.
Am i thinking about this the right way? I've tried to have the fetch call in the same ponent as AutoComplete but haven't gotten it to work as i want it to. How would you setup the ponent structure if you'd be solving this issue for example?
Feel free to ask if there’re any confusions.
Thanks
Share Improve this question asked Oct 16, 2019 at 12:52 erikos93erikos93 6872 gold badges13 silver badges28 bronze badges1 Answer
Reset to default 3These are my assumptions about the above code, let me know if any are incorrect.
- You're doing an initial API request for a list of movies by search term. That search term is given outside of the code above and doesn't change by any mechanism above.
- You're further filtering the data returned by the API request dynamically via the
AutoComplete
ponent.
Most of your React code here looks pretty good, but I've got some remendations:
- In general, I find it's best to keep the list of things pletely separate, so I would create a separate
MovieList
function ponent that simply takes an array ofmovie
objects, and atitleFilter
string and renders them. ponentDidMount
doesn't take arguments.searchTerm
should be a prop ofFirstPageComponent
(presumably set by the parent ponent by some kind of selection or user input)- I'd rename
AutoComplete
toTitleFilter
or the like. It's not really auto-pleting, it's more of a filter field. - This line:
if (loading && userInput) {
will result in displaying no movies when the user hasn't yet filled in text into the filter field. I'm not sure that is what you would want. - This line is a bit misleading:
<p>Couldn't find any movies. Try searching for another movie.</p>
because you could have just filtered out all the results returned. In this case, it wasn't that you couldn't find movies, it's that your filter is too restrictive. - For the
onClick
handler, I'm not sure such a general approach is best. It could create a strange interaction if you were to somehow click on the<ul>
, for example. To improve this, I'd remend each movie row ponent implement its ownonClick
and call a function to set the filter. - You're going to have to deal with pagination on this, I'd bet. Check your API and see what it does for that and make sure you're accounting for it.
Small nits:
userInput: e.currentTarget.value,
could be shortened touserInput,
, which is shorthand foruserInput: userInput,
(you already have a variable nameduserInput
.)- For
key
props, an actual unique id would be better, in the case you change your filtering and index numbers don't line up with the unique rows you're rendering anymore. - I like to go ahead and create a separate ponent for each row, so I'd make a
MovieRow
function ponent which takes amovie
object and renders it. It just keeps the code a bit cleaner. (and makes #6 above easier to implement)