Searching for elements and setting pagination only on these searched elements - javascript

I have this problem: for example, I will search for the phrase ab for 45 results. How to set pagination to include only 45 results, not all elements? He is currently looking for 45 items for me. But when I go to the other side, it includes all the elements for me, not just the ones I searched for.
class App extends Component {
constructor() {
super();
this.state = {
todos: []
};
}
searchTodo = debounce((query) => {
this.setState({
query
})
this.getTodo((query), 200)
})
getTodos = (number) => {
const params = {
expand: 'project',
'per-page': 20,
'page': number
}
axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: "GET"
})
.then(res => {
this.setState({
todos: res.data
});
})
.catch(error => {
console.log(error);
})
}
handlePage = (number) => {
this.getTodos(number);
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
<SearchInput onChange={e => this.searchTodo(e)} />
</p>
<Pagination
itemsCountPerPage={20}
totalItemsCount={this.state.todos.length}
onChange={this.handlePage}
/>
</div>
);
}
}

Related

Why isn't the map function iterating over the array?

I am using React, which I am new to and it says to use the map method to iterate over the array. When I do that the entire array logs to the console and not the individual words I want.
I am trying to call stock symbols when a user types them in the text box. But under the function getSymbol() the state of symbol is still an array even when I used map in render. Can anyone point me in the right direction? Or what can I do to get single elements.
Here is my code:
class Symbol extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: '',
symbol: [],
isLoaded: false
}
}
typeSymbol = (e) => {
this.setState({
userInput: e.target.value.toUpperCase()
}, () => {
console.log(this.state.userInput)
})
}
getSymbol = (e) => {
e.preventDefault(),
this.setState({
symbol: this.state.symbol
}, () => {
console.log(this.state.symbol)
})
}
componentDidMount() {
fetch(`https://finnhub.io/api/v1/stock/symbol?exchange=US&token=${key}`)
.then(res => res.json())
.then(
(results) => {
this.setState({
isLoaded: true,
symbol: results
});
// console.log(this.state.symbol)
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { symbol, userInput } = this.state
if (userInput === symbol) {
return (
symbol.map((symbol, i) => (<span className="symbol" key={i}>{symbol.displaySymbol}</span>)),
console.log('same')
)
} else {
return (
<div className="enterstock">
<h1 className="title">Enter Stock Symbol</h1>
<form className="inputfields" onSubmit={this.getSymbol}>
<input type="text" className="symfields" name="symbolname" onChange={this.typeSymbol}></input>
<button type="submit" className="btn">Send</button>
</form>
</div >
)
}
}
}
You're using symbol in two different ways, and they're colliding. Try renaming the parameter of your map() function:
symbol.map((s, i) => (<span className="symbol" key={i}>{s.displaySymbol}</span>)),
symbol is not a reserved word in JavaScript, so you should be good there.
The problem is with your if block. The map method works fine. What is happening is you are comparing the userInput(String) to symbol(an array) it will not work. I don't know what check you're trying to do. But if it is to check whether the userInput is in the array you're doing it wrongly.
import React from "react";
class Symbol extends React.Component {
constructor(props) {
super(props);
this.state = {
userInput: "",
symbol: [],
isFiltered: false,
isLoaded: false,
};
}
typeSymbol = (e) => {
this.setState(
{
userInput: e.target.value.toUpperCase(),
},
() => {
console.log(this.state.userInput);
}
);
};
getSymbol = (e) => {
e.preventDefault();
const filter = this.state.symbol.filter(
(el) => el.displaySymbol === this.state.userInput
);
// console.log(filter);
this.setState(
{
symbol: filter,
isFiltered: true,
},
() => {
console.log(this.state.symbol);
}
);
};
componentDidMount() {
fetch(`https://finnhub.io/api/v1/stock/symbolexchange=US&token=${key}`)
.then((res) => res.json())
.then(
(results) => {
this.setState({
isLoaded: true,
symbol: results,
});
console.log(this.state.symbol);
},
(error) => {
this.setState({
isLoaded: true,
error,
});
}
);
}
render() {
const { symbol, userInput } = this.state;
//console.log(userInput);
if (this.state.isFiltered) {
return symbol.map((symbol, i) => {
return (
<span className="symbol" key={i}>
{symbol.displaySymbol}
</span>
);
});
} else {
return (
<div className="enterstock">
<h1 className="title">Enter Stock Symbol</h1>
<form className="inputfields" onSubmit={this.getSymbol}>
<input
type="text"
className="symfields"
name="symbolname"
onChange={this.typeSymbol}
></input>
<button type="submit" className="btn">
Send
</button>
</form>
</div>
);
}
}
}
export default Symbol;

Why my methods for React function components don't work

I am developing a project with Moviedb api. I created the movie list under the name Movie component. I make my api requests in this component. From the Movie component to the MovieInfo component, I send the release date of the movie and the genres of the movie via props.
But I cannot apply substring and map methods to these properties that come to me in the MovieInfo component with props.
class Movie extends Component {
state = {
movie: [],
loading: false,
actors: [],
directors: [],
visible : 6 // This state is for how many actors rendered.
}
componentDidMount() {
this.setState({
loading: true
})
let moviesEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}?api_key=${API_KEY}&language=tr`
let creditsEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}/credits?api_key=${API_KEY}`;
this.getMovieWithId(moviesEndPoint);
this.getDirectorsAndActors(creditsEndPoint);
}
getMovieWithId = moviesEndPoint => {
fetch(moviesEndPoint)
.then(response => response.json())
.then((movie) => {
// console.log(movie);
if (movie.overview !== "" && !movie.status_code) {
this.setState({
movie,
loading: false
})
}
else { // if have not turkish overview fetch this
let engEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}?api_key=${API_KEY}`
fetch(engEndPoint)
.then(response => response.json())
.then((movie) => {
this.setState({
movie
})
})
}
})
}
getDirectorsAndActors = creditsEndPoint => {
fetch(creditsEndPoint)
.then(response => response.json())
.then((credits) => {
// console.log(credits)
const filterDirector = credits.crew.filter(person => person.job === "Director"); // filter directors from all employees
// console.log(filterDirector)
this.setState({
actors: credits.cast,
directors: filterDirector[0].name,
loading: false
})
})
}
render() {
const { movie, loading, actors, directors, visible } = this.state
const { location } = this.props
return (
<>
{
loading ? <Spinner /> : null
}
{this.state.movie ?
<MovieInfo
movieInfo={movie}
actors={actors}
directors={directors}
searchWord={location.searchWord}
visible = {visible}
loadMore = {this.loadMore}
loading = {loading}
/> : null
}
{
!actors && !loading ? <h1>Film Bulunamadı! </h1> : null
}
</>
)
}
}
This is the non-working code inside my MovieInfo component and my errors like this :
TypeError: Cannot read property 'substring' of undefined
TypeError: Cannot read property 'map' of undefined
const MovieInfo = ({ movieInfo, searchWord, directors, actors, visible, loadMore, loading }) => {
const editReleaseDate = (date) => { //? Idk why doesn't work !
// return date.substring(5).split("-").concat(date.substring(0,4)).join("/")
return date
// console.log(date)
// return date;
}
return (
<Col sm={5} className="movieInfo p-4 animated fadeInRightBig">
<p className = "movie-title" > {movieInfo.title} </p>
<h5 className = "mb-4 text-warning">Yayınlanma Tarihi: <span className = "text-light">{editReleaseDate(movieInfo.release_date)}</span></h5>
<h5 className = "text-warning">Açıklama</h5>
<p>{movieInfo.overview} </p>
<ProgressBar label={`IMDB: ${movieInfo.vote_average}`} animated now = {`${movieInfo.vote_average}`} min={0} max={10} />
<h5 className = "text-warning mt-3">Türü:
{ //? Idk why doesn't work !
// movieInfo.genres.map((genre, i) => {
// return <span key = {i} >{genre.name}</span>
// })
}
</h5>
<h5 className ="mt-2 text-warning">Yönetmen: <span className = "text-light">{directors} </span> </h5>
<div> <i className="fas fa-film fa-5x"></i> </div>
</Col>
)
You have 1 loading state for 2 api calls so once one is finishing it is telling the component that it is done loading even if the second has finished. I split it up into 2 different loading states, loadingMovie & loadingActors.
class Movie extends Component {
state = {
movie: [],
loadingMovie: false,
loadingActors: false,
actors: [],
directors: [],
visible : 6 // This state is for how many actors rendered.
};
componentDidMount() {
this.setState({
...this.state,
loadingMovie: true,
loadingActors: true,
});
let moviesEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}?api_key=${API_KEY}&language=tr`;
let creditsEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}/credits?api_key=${API_KEY}`;
this.getMovieWithId(moviesEndPoint);
this.getDirectorsAndActors(creditsEndPoint);
}
getMovieWithId = moviesEndPoint => {
fetch(moviesEndPoint)
.then(response => response.json())
.then((movie) => {
// console.log(movie);
if (movie.overview !== "" && !movie.status_code) {
this.setState({
movie,
loadingMovie: false
})
}
else { // if have not turkish overview fetch this
let engEndPoint = `${BASE_URL}/movie/${this.props.match.params.movieId}?api_key=${API_KEY}`
fetch(engEndPoint)
.then(response => response.json())
.then((movie) => {
this.setState({
movie,
loadingMovie: false
});
})
}
})
}
getDirectorsAndActors = creditsEndPoint => {
fetch(creditsEndPoint)
.then(response => response.json())
.then((credits) => {
// console.log(credits)
if (credits && credits.crew) {
const filterDirector = credits.crew.filter(person => person.job === "Director"); // filter directors from all employees
// console.log(filterDirector)
this.setState({
actors: credits.cast,
directors: filterDirector[0].name,
loadingActors: false
})
} else {
console.log('bad credits');
}
})
}
render() {
const { movie, loadingActors, loadingMovie, actors, directors, visible } = this.state;
const { location } = this.props;
return (
<>
{loadingActors || loadingMovie ? <Spinner /> :
(movie.length && actors.length) ?
<MovieInfo
movieInfo={movie}
actors={actors}
directors={directors}
searchWord={location.searchWord}
visible = {visible}
loadMore = {this.loadMore}
loading = {(loadingActors || loadingMovie)}
/> : null
}
{
actors.length && !loadingActors ? <h1>Film Bulunamadı! </h1> : null
}
</>
);
}
}

Cannot delete input content in Typeahead react-bootstrap

Trying to delete the contents of the input field by pressing backspace. However, nothing can be deleted. When I delete onChange = {this.handleSelectContractor} the field can be cleared, but after adding onChange = {this.handleSelectContractor} nothing can be done.
Pass selected todo to the handleSelect and assign it to the variable selected. The variable selected should appear in the input field
Demo here: https://stackblitz.com/edit/react-agfvwn?file=index.js
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
selected: [],
todo: {},
todo2: 'ddd'
};
}
componentDidMount() {
this.getTodos().then(() => {
const todo = this.state.todos.find(todo => todo.id === 4);
this.setState({
selected: todo ? [todo] : []
})
})
}
getTodos = () => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: "GET"
})
.then(res => {
this.setState({
todos: res.data
});
})
.catch(error => {
console.log(error);
})
}
handleSelect = (todo) => {
let newArray = [...this.state.selected];
newArray.push(todo)
if(todo) {
this.setState({
todo: newArray
})
} else {
this.setState({
todo2: ''
})
}
}
render() {
console.log(this.state.todo)
return (
<div>
<Typeahead
id={'sasas'}
selected={this.state.selected}
labelKey="title"
onChange={this.handleSelect}
options={this.state.todos}
ref={(ref) => this._typeahead = ref}
/>
</div>
);
}

How to show information from API when using search box in ReactJS?

I'm using the Star Wars API to build a React JS project. The aim of my app is to be able to search for characters.
Here is my code for the search component in the my app.
At the moment I'm able to retrieve data the API and show the information on the page but I can't work out how to show this information when it's searched for.
Any ideas?
import React, { Component } from 'react';
class Search extends Component {
constructor(props){
super(props)
this.state = {
query:'',
peoples: [],
}
}
onChange (e){
this.setState({
query: e.target.value
})
if(this.state.query && this.state.query.length > 1) {
if(this.state.query.length % 2 === 0){
this.componentDidMount()
}
}
}
componentDidMount(){
const url = "https://swapi.co/api/people/";
fetch (url,{
method:'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return(
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({peoples: peoples});
console.log("state", peoples)
})
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search
You could put your fetch in a separate function instead of in componentDidMount and call that when the component mounts and when your query changes.
Since you might be creating multiple requests if the user types quickly, you could use a debounce to only send one request, or use something that verifies that you always use the result of the latest request, like e.g. a token.
Example
class Search extends Component {
token = null;
state = {
query: "",
people: []
};
onChange = e => {
const { value } = e.target;
this.setState({
query: value
});
this.search(value);
};
search = query => {
const url = `https://swapi.co/api/people?search=${query}`;
const token = {};
this.token = token;
fetch(url)
.then(results => results.json())
.then(data => {
if (this.token === token) {
this.setState({ people: data.results });
}
});
};
componentDidMount() {
this.search("");
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange}
/>
{this.state.people.map(person => (
<ul key={person.name}>
<li>{person.name}</li>
</ul>
))}
</form>
);
}
}
You have to define it in diff function to manage easy.
import React, { Component } from 'react';
class Search extends Component {
constructor(props) {
super(props)
this.state = {
query: null,
peoples: [],
}
}
componentDidMount() {
this.serachPeople(this.state.query);
}
onChange(e) {
this.setState({ query: e.target.value }, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.serachPeople(this.state.query);
}
} else {
this.serachPeople(this.state.query);
}
})
}
serachPeople(query) {
const url = "https://swapi.co/api/people/";
if (query) {
// if get value ion query so filter the data based on the query.
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.filter(people => people.name === query).map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
} else {
fetch(url, {
method: 'GET'
}).then(results => {
return results.json();
}).then(data => {
let peoples = data.results.map((people) => {
return (
<ul key={people.name}>
<li>{people.name}</li>
</ul>
)
})
this.setState({ peoples: peoples });
console.log("state", peoples)
})
}
}
render() {
return (
<form>
<input
type="text"
className="search-box"
placeholder="Search for..."
onChange={this.onChange.bind(this)}
/>
{this.state.peoples}
</form>
)
}
}
export default Search;
I hope this will help for u. Let me know if u have any query.

Issue with react onClick

My app has an onClick that should be rendering more gifs. However, it does it once and then stops. Also, the onClick deletes all the gifs that were already on the page. Anyone know what I'm doing wrong?
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
};
}
componentDidMount() {
this.searchGifs('kittens');
}
searchGifs = (searchQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${searchQuery}&limit=12&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
searchMoreGifs = (offsetQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${offsetQuery}&limit=12&offset=${this.state.results.length}&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
render() {
return (
<main className="app">
<Header />
<SearchForm startSearch={this.searchGifs} />
<ResultList gifs={this.state.results} />
<LoadMore gifs={this.state.results} searchMore={this.searchMoreGifs} />
</main>
);
}
}
and here is the onClick:
class LoadMore extends React.Component {
render(props) {
return(
<button onClick={this.props.searchMore}>Load More</button>
);
}
}
export default LoadMore;
Each time you call this.setState({results: something}) you completely overwrite the previous state of results.You want to take the array that is in this.state.results and concat it with the new results.
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
// I also suggest moving the searchQuery to the state so it can be used in both the offset and the original search
searchQuery: 'kittens'
};
}
componentDidMount() {
this.searchGifs(this.state.searchQuery);
}
searchGifs = (searchQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${searchQuery}&limit=12&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
results: response.data,
});
});
}
searchMoreGifs = (offsetQuery) => {
fetch(`http://api.giphy.com/v1/gifs/search?q=${offsetQuery}&limit=12&offset=${this.state.results.length}&api_key=dc6zaTOxFJmzC`).then(data => data.json())
.then(response => {
this.setState({
// You were overwriting the old results with new data every time you ran this function
results: this.state.results.concat(response.data),
});
});
}
render() {
return (
<main className="app">
<Header />
<SearchForm startSearch={this.searchGifs} />
<ResultList gifs={this.state.results} />
{/* You also need to pass the offsetQuery to the LoadMore component so that the searchMore function can use it*/}
<LoadMore searchMore={this.searchMoreGifs} offsetQuery={this.state.searchQuery} />
</main>
);
}
}
class LoadMore extends React.Component {
render(props) {
const {offsetQuery, searchMore} = this.props
return (
<button onClick={() => searchMore(offsetQuery)}>Load More</button>
);
}
}
export default LoadMore;

Categories

Resources