How to make a search box working properly on react js - javascript

I am a beginner and I am learning React JS. I am doing a basic project in React. I have a search box. When I search for something my page will show search related content.
Here I used includes() to make my search workable. But it is not working.
I have indicated the line of setting a new array of dogs at line 40.
Pleae have a look to my code.
Here is my codes:
Home.jsx
import React, { useState, useEffect } from 'react';
import Dogs from './components/Dogs/Dogs';
import Search from './components/search/Search';
const Home = () => {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [value, setValue] = useState('');
const [search, setSearch] = useState('');
const [dogs, setDogs] = useState([]);
useEffect(() => {
fetch('https://api.thedogapi.com/v1/breeds', {
headers: {
Authorization: '4139956d-7014-4fa8-b295-8e361418c807',
},
})
.then(response => response.json())
.then(
data => {
setIsLoaded(true);
setDogs(data);
},
error => {
setIsLoaded(true);
setError(error);
}
);
}, []);
const handleChange = event => {
setValue(event.target.value);
};
const handleSearch = () => {
setSearch(value);
setValue('');
};
// This is the function to making a new array of dogs of searched name
useEffect(() => {
const newDogList = dogs.filter(dog => {
if (dog.name.includes(search)) dog
});
setDogs(newDogList);
}, [search]);
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div className="container">
<Search
value={value}
handleChange={handleChange}
handleSearch={handleSearch}
/>
<Dogs dogs={dogs} />
</div>
);
}
};
export default Home;
How can I make it workable?

Related

React simple useEffect infinit loop

I do not understand why this code is an infinit loop. I this it is because I update my state in else if condition. But i'd like to update an array of urls when getting new api answer.
import React, { useState, useEffect } from 'react';
function getShortenUrl() {
const [urls, setUrls] = useState([])
const [error, setError] = useState(null);
const [items, setItems] = useState({});
useEffect(() => {
fetch("https://api.shrtco.de/v2/shorten?url=https://stackoverflow.com/questions/37435334/correct-way-to-push-into-state-array")
.then(res => res.json())
.then(
(result) => {
setItems(result);
},
(error) => {
setError(error);
}
)
}, [])
if (error) {
return <div>Error : {error.message}</div>;
} else if (!items.result) {
return <div>Loading...</div>;
console.log("no item");
} else {
setUrls([urls, items]);
console.log("items", items);
console.log("urls", urls);
return <ul> {items.result.short_link}</ul>;
}
}
export default getShortenUrl;
I am kindof lost when it comes to state actually. I do not understand how I can create an array of urls and be able to use it in other components.
You may have some mistakes here
.then(
(result) => {
setItems(result);
},
(error) => {
setError(error);
}
)
Change it into
.then((result) => {
setItems(result);
setUrls([...urls, result])
})
.catch((error) => {
setError(error);
})
And also remove the line setUrls([urls, items]);
If you are new to react then we make Loading state for API Call fetch and you are setting state in else and when state updates component re-renders so on every component re-render you are updating state which is causing infinite loop. Try this
import React, { useState, useEffect } from 'react';
function getShortenUrl() {
const [urls, setUrls] = useState([])
const [error, setError] = useState(null);
// const [items, setItems] = useState({}); // remove it because you are using urls and items state for same purposes
const [loading,setLoading]=useState(false);
useEffect(() => {
setLoading(true);
fetch("https://api.shrtco.de/v2/shorten?url=https://stackoverflow.com/questions/37435334/correct-way-to-push-into-state-array")
.then(res => res.json())
.then(
(result) => {
// setItems(result); // remove it
setUrls(result);
setLoading(false);
},
(error) => {
setError(error);
setLoading(false);
}
)
}, [])
if(loading){
return <div>Loading...</div>;
}
if (error) {
return <div>Error : {error.message}</div>;
} else {
return <ul> {urls?.result?.short_link}</ul>;
}
}
export default getShortenUrl;
Try to use a conditional rendering instead of if else statement:
import React, { useState, useEffect } from 'react';
function getShortenUrl() {
const [urls, setUrls] = useState([])
const [error, setError] = useState(null);
const [items, setItems] = useState({});
useEffect(() => {
fetch("https://api.shrtco.de/v2/shorten?url=https://stackoverflow.com/questions/37435334/correct-way-to-push-into-state-array")
.then(res => res.json())
.then(
(result) => {
setItems(result);
},
(error) => {
setError(error);
}
)
}, [])
return (
<>
{error&& <div>Error : {error.message}</div>}
{!items.result && <div>Loading...</div> }
{items.result && <ul> {items.result.short_link}</ul>}
</>
)
}
export default getShortenUrl;

Why can't I render multiple cards using map?

I'm trying to render multiple cards by pulling data from the API. But the return is an array, I don't understand why the map is not working.
const CharacterCard = () => {
const [showModal, setShowModal] = useState(false)
const openModal = () => {
setShowModal(prev => !prev)
}
const characters = useRequestData([], `${BASE_URL}/characters`)
const renderCard = characters.map((character) => {
return (
<CardContainer key={character._id} imageUrl={character.imageUrl}/>
)
})
return (
<Container>
{renderCard}
<ModalScreen showModal={showModal} setShowModal={setShowModal} />
</Container>
)
}
export default CharacterCard
The hook is this
import { useEffect, useState } from "react"
import axios from "axios"
const useRequestData = (initialState, url) => {
const [data, setData] = useState(initialState)
useEffect(() => {
axios.get(url)
.then((res) => {
setData(res.data)
})
.catch((err) => {
console.log(err.data)
})
}, [url])
return (data)
}
export default useRequestData
console error image
requisition return image
API: https://disneyapi.dev/docs
Looks like the default value of the characters is undefined.
So something like (characters || []).map.. will help I think.
For deeper look at this you can debug useRequestData hook, as I can't see the source of that hook from you example

Search function now working in React photo gallary

Working on a small application that takes a pexels api and displays photos dynamically. When I send the search request for my api to fectch based on the new params, it does actually update the page with new photos but not the ones based on the params. I though I got the search function correct, maybe it's cause I'm not using it in a useEffect? But if I did use it in a useEffect, I wouldn't be able to set it on the onClick handle. I tried to console.log the query I was getting from the onChange but it doesn't seem like it's getting the result. What am I doing wrong?
import { useState, useEffect } from 'react'
import pexelsApi from './components/pexelsApi'
import './App.css'
const App = () => {
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(false);
const [nextPage, setNextPage] = useState(1);
const [perPage, setPerPage] = useState(25);
const [query, setQuery] = useState('');
const [error, setError] = useState('');
useEffect(() => {
const getImages = async () => {
setLoading(true);
await pexelsApi.get(`/v1/curated?page=${nextPage}&per_page=${perPage}`)
.then(res => {
setImages([...images, ...res.data.photos]);
setLoading(false);
}).catch(er => {
if (er.response) {
const error = er.response.status === 404 ? 'Page not found' : 'Something wrong has happened';
setError(error);
setLoading(false);
console.log(error);
}
});
}
getImages();
}, [nextPage, perPage]);
const handleLoadMoreClick = () => setNextPage(nextPage + 1)
const search = async (query) => {
setLoading(true);
await pexelsApi.get(`/v1/search?query=${query}&per_page=${perPage}`)
.then(res => {
setImages([...res.data.photos]);
console.log(res.data)
setLoading(false);
console.log(query)
})
}
if (!images) {
return <div>Loading</div>
}
return (
<>
<div>
<input type='text' onChange={(event) => setQuery(event.target.value)} />
<button onClick={search}>Search</button>
</div>
<div className='image-grid'>
{images.map((image) => <img key={image.id} src={image.src.original} alt={image.alt} />)}
</div>
<div className='load'>
{nextPage && <button onClick={handleLoadMoreClick}>Load More Photos</button>}
</div>
</>
)
};
export default App
import axios from 'axios';
export default axios.create({
baseURL: `https://api.pexels.com`,
headers: {
Authorization: process.env.REACT_APP_API_KEY
}
});
Your main issue is that you've set query as an argument to your search function but never pass anything. You can just remove the arg to have it use the query state instead but you'll then need to handle pagination...
// Helper functions
const getCuratedImages = () =>
pexelsApi.get("/v1/curated", {
params: {
page: nextPage,
per_page: perPage
}
}).then(r => r.data.photos)
const getSearchImages = (page = nextPage) =>
pexelsApi.get("/v1/search", {
params: {
query,
page,
per_page: perPage
}
}).then(r => r.data.photos)
// initial render effect
useEffect(() => {
setLoading(true)
getCuratedImages().then(photos => {
setImages(photos)
setLoading(false)
})
}, [])
// search onClick handler
const search = async () => {
setNextPage(1)
setLoading(true)
setImages(await getSearchImages(1)) // directly load page 1
setLoading(false)
}
// handle pagination parameter changes
useEffect(() => {
// only action for subsequent pages
if (nextPage > 1) {
setLoading(true)
const promise = query
? getSearchImages()
: getCuratedImages()
promise.then(photos => {
setImages([...images, ...photos])
setLoading(false)
})
}
}, [ nextPage ])
The reason I'm passing in page = 1 in the search function is because the setNextPage(1) won't have completed for that first page load.

How to render a list from a JSON url? - ReactJS hooks

I want to render a list from a JSON URL. However, I have the following error: Objects are not valid as a React child (found: TypeError: Failed to fetch). If you meant to render a collection of children, use an array instead. What am I going wrong? Thanks for your answer
//hooks.tsx
import { useEffect, useState } from 'react'
export const useFetch = () => {
const [data, setData] = useState([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
useEffect(() => {
setLoading(true)
setError(null)
fetch('https://jsonkeeper.com/b/Z51B')
.then(res => res.json())
.then(json => {
setLoading(false)
if (json.data) {
setData(json.data)
} else {
setData([])
}
})
.catch(err => {
setError(err)
setLoading(false)
})
}, [])
return { data, loading, error }
}
//index.tsx
import React from 'react';
import { useFetch } from "./hooks.js";
import {CardItem} from './card';
export const List = () => {
const { data, loading, error } = useFetch()
if (loading) return <div>Loading...</div>
if (error) return <div>{error}</div>
return (
<>
<ul>
{data.map((item: any, index: any) => (
<li key={index}>
{item.names.map((name: any) => {
return <CardItem
family={item.family}
name={name}
/>
})
}
</li>
))}
</ul>
</>
);
};
I guess the problem is with your setError(err) in catch block of your custom hook. It should be setError(err.message).

How make work a search filter in react for node array

Hi guys i make a search filter in react for an array i get via my node server, but i got this error:
×
TypeError: props.filteredCharacters.map is not a function in components/CharacterList/index.js:6
Here the 2 files:
import React, { useEffect, useState } from 'react'
import SearchBox from '../SearchBox'
import CharacterList from '../CharacterList'
const SearchDisney = () => {
const [inputs, setInputs] = useState('');
const [btn, setBtn] = useState(false);
const [apiResponse, setApiResponse] = useState([]);
const [searchCharacter, setSearchCharacter] = useState('');
useEffect(() => {
callAPI();
if (inputs.length > 2) {
setBtn(true)
} else if (btn) {
setBtn(false)
}
}, [inputs, btn])
const callAPI = () => {
fetch("http://localhost:9000/disneyCharacter")
.then(res => res.json())
.then(res => setApiResponse(res))
}
const handleInput = (e) => {
setSearchCharacter(e.target.value)
}
const filteredCharacters = () => {
apiResponse.filter((character) => {
return character.name.toLowerCase().includes(searchCharacter.toLowerCase())
})
}
return (
<div className="search-container">
<h1>Personnage Infos</h1>
<SearchBox handleInput={handleInput} />
<CharacterList filteredCharacters={filteredCharacters} />
</div>
)
}
export default React.memo(SearchDisney)
And the CharacterList:
import React from 'react'
import Character from '../Character'
const CharacterList = (props) => {
const characters = props.filteredCharacters.map((character, id) => {
return <Character key={id} name={character.name} username={character.username} yearCreation={character.yearCreation}/>
})
return (
<div>
{ characters }
</div>
)
}
export default CharacterList
i can display the array in the first file but now i want to make search filter and got this error, any advice to get ride of this error?
Looks like there are 2 things you need to fix here:
on the SearchDisney component, you are not returning anything from the filteredCharacters function. Here is the fix:
const filteredCharacters = () => {
//need to return this
return apiResponse.filter((character) => {
return character.name.toLowerCase().includes(searchCharacter.toLowerCase())
})
}
Addtionally, in order for CharacterList to recieve the filteredCharacters prop as an array - you have to call the filteredCharacters function which returns this array, for example, like this:
<div className="search-container">
<h1>Personnage Infos</h1>
<SearchBox handleInput={handleInput} />
//call the function here:
<CharacterList filteredCharacters={filteredCharacters()} />
</div>

Categories

Resources