In this app, I'm fetching images from the Unsplash API (with an Express back end, React front end). On page load, general images appear (rendered inside the react-infinite-scroll-component). If you search for a term, the search state changes from false to true. I use a ternary operator to conditionally render the images inside the infinite scroll component:
{this.state.newSearch || this.state.search ? this.state.searchImages.map(image =>
<Image key={image.id} image={image} />
) : this.state.images.map(image =>
<Image key={image.id} image={image} />
)}
The images that first loaded (from componentDidMount) are replaced after you type in the input and press enter, but if you type something else and hit enter, the new search results are instead added to the end. Instead, I'd like to replace the previous search results. I've been struggling to figure out how to accomplish that. I tried adding the newSearch state and setting searchImages: [] and searchStart: 1 in handleSubmit, but neither are making any difference. As a React novice I also tried setting newSearch back to false after the image render in the infinite scroll component, which broke the app due to the infinite loop.
Any help would be greatly appreciated.
Parent:
export class Images extends Component {
state = {
images: [],
searchImages: [],
count: 30,
start: 1,
searchStart: 1,
term: '',
search: false,
newSearch: false
};
componentDidMount() {
const { count, start } = this.state;
axios
.get(`/api/photos?count=${count}&start=${start}`)
.then(res => this.setState({ images: res.data }));
}
fetchImages = () => {
const { count, start, images } = this.state;
// Will be at 1, then 31 (1 + 30), then 61 (31 + 30), and so forth
this.setState({ start: start + count });
axios
.get(`/api/photos?count=${count}&start=${start}`)
.then(res =>
this.setState({ images: images.concat(res.data) })
);
}
fetchSearchImages = () => {
const { searchStart, count, term, searchImages } = this.state;
this.setState({
searchStart: searchStart + count
});
axios
.get(`/api/photos/search?term=${term}&count=${count}&start=${searchStart}`)
.then(res =>
this.setState({
searchImages: searchImages.concat(res.data.results)
})
);
}
handleSubmit = () => {
this.setState({
searchImages: [],
searchStart: 1,
search: true,
newSearch: true
});
this.fetchSearchImages();
}
handleInputChange = (e) => {
this.setState({
term: e
});
}
render() {
return (
<>
<SearchInput onSearch={this.handleInputChange} onFormSubmit={this.handleSubmit} term={this.state.term} />
<div className="images">
<InfiniteScroll
dataLength={this.state.newSearch || this.state.search ? this.state.searchImages.length : this.state.images.length}
next={this.state.search ? this.fetchSearchImages : this.fetchImages}
hasMore={true}
loader={
<div className="loader-dots">
<span className="loader-dot"></span>
<span className="loader-dot"></span>
<span className="loader-dot"></span>
<span className="loader-dot"></span>
</div>
}
>
{this.state.newSearch || this.state.search ? this.state.searchImages.map(image =>
<Image key={image.id} image={image} />
) : this.state.images.map(image =>
<Image key={image.id} image={image} />
)}
</InfiniteScroll>
</div>
</>
);
}
}
Child search component:
const SearchInput = props => {
const onSubmit = e => {
// Prevents GET request/page refresh on submit
e.preventDefault();
props.onFormSubmit();
};
return (
<form onSubmit={onSubmit}>
<div className="control">
<input autoFocus value={props.term} onChange={e => props.onSearch(e.target.value)} className="input" type="text" placeholder="Search" />
</div>
</form>
);
}