I'm trying to capture some suggestions for my input field, i can log them without a problem, but i can't map it and autocomplete on my input type field. trying to understand what i'm doing wrong.
const Lista = () => {
const [search, setSearch] = useState("");
useEffect(() => {
handleSearch();
}, []);
async function handleSearch() {
const response = await fetch("/api/product");
const search = await response.json();
setSearch(search);
console.log(search);
}
if (!search) return <p>Loading</p>;
return (
<>
<section>
<div className="listsContainer">
<div className="cartContainer">
<form className="formContainer" onSubmit={handleSubmit}>
<div className="inputs">
<div>
<label>Product</label>
<input
required
onChange={(e) => setSearch(e.target.value)}
/>
<div>
{search
.filter((item) => {
return search.toLowerCase() === ""
? null
: item.name.toLowerCase().includes(search);
})
.map((item) => (
<div key={item.id}>
<h1>{item.name}</h1>
</div>
))}
</div>
you are mapping 'search' as the response while it contains the user input value.
you should add a new locale state for storing the response and then use it for the mapping.
you should notice that filter is an Array method and will not work on Json. You should filer Object.values(yourJson).
see example bellow:
const Lista = () => {
const [search, setSearch] = useState("");
const [productResponse, setproductResponse] = useState({});
useEffect(() => {
handleSearch();
}, []);
async function handleSearch() {
const response = await fetch("/api/product");
const resToJson= await response.json();
setproductResponse(resToJson);
console.log(resToJson);
}
if (!productResponse) return <p>Loading</p>;
return (
<>
<section>
<div className="listsContainer">
<div className="cartContainer">
<form className="formContainer" onSubmit={handleSubmit}>
<div className="inputs">
<div>
<label>Product</label>
<input
required
onChange={(e) => setSearch(e.target.value)}
/>
<div>
{Object.values(productResponse)?.
.filter((item) => {
return search.toLowerCase() === ""
? null
: item.name.toLowerCase().includes(search);
})
I added useEffect to the xml processing function and it fixed it right up.
useEffect(() => {
if (inputValue.length > 50) {
setXmlValue((xmlValue) => loadPoBXml(inputValue));
}
}, [inputValue])
I am rather stumped with this problem.
I am writing a web app that converts a base64 string into a xml file and extracts data from that xml file.
When I first run the app after a npm start or after a browser refresh the function that converts the string does not complete correctly. If I trigger the function a second time, it works, and will continue to work until I do a refresh. I believe the problem lies within the populateData function and have tried to make it work as async, but that made the problem considerably worse.
I have included a screenshot of my console log and the code is detailed below (please note I have for brevity removed some parts of the code):
Buffer.from('anything','base64');
export default function Home() {
// State to store value from the input field
const [xmlValue, setXmlValue] = useState([]);
const [buildValue, setBuildValue] = useState([]);
const [buildData, setBuildData] = useState([]);
const [mainSkill, setMainSkill] = useState("");
const [inputValue, setInputValue] = useState("");
const xmlData = useSelector(state => state)
// redux data management
const dispatch = useDispatch();
function populateData(str) {
let data = ""
if (str.includes("https://")) {
data = httpGet(str)
}
else {
data = str
}
if(typeof xmlData !== "unknown") {
console.log("decode was called")
// get xml data as an array
console.log("pob data was called")
setXmlValue((xmlValue) => loadPoBXml(data))
// populate character data
console.log("handle character called")
handleChar(xmlValue)
// find the main dps skill
findMainSkill(xmlValue)
// populate the store
dispatch(populateGems(xmlValue))
console.log("store loaded")
}
else {
console.log("no decode called")
handleChar(xmlData)
findMainSkill(xmlData)
}
}
function findMainSkill(event) {
// removed code
};
function populateBuildData(data) {
//removed code
}
function handleChar(event) {
console.log(event)
let temp = event.getElementsByTagName('Build')
setBuildValue((buildValue) => temp);
populateBuildData(temp)
};
// Input Field handler
const handleUserInput = (e) => {
setInputValue(e.target.value);
};
// Reset Input Field handler
const resetInputField = () => {
setInputValue("");
setBuildValue([])
setBuildData([])
};
return (
<>
<div className="InsideContent">
<Input className="input1"
placeholder="Paste PoB Code"
name="pobCode"
value={inputValue}
onChange={handleUserInput} />
</div>
<div className="InsideContent">
<Button1 onClick={() => populateData(inputValue)}>Confirm</Button1>
<Button2 onClick={resetInputField}>Reset</Button2>
</div>
{buildValue.length > 0 && (
<div className="card">
<div className="divider"></div>
{buildValue.map((item, i) => (
<ul key={`item${i}`}>
<div className="acend">
<img src={require("../images/" + item.attributes.ascendClassName +".png")}></img>
</div>
<div className="leftInfo">
<p>{item.attributes.ascendClassName} Level: {item.attributes.level}</p>
<div className="leftInfoSmall">
<p>{mainSkill} {buildData.CombinedDPS} DPS</p>
<p>Life: <span className="redText">{buildData.Life}</span> ES: <span className="blueText">{buildData.EnergyShield}</span> Ward: <span className="grayText">{buildData.Ward}</span></p>
</div>
</div>
<div className="rightInfo">
<p>EHP: {buildData.TotalEHP} Max Hit: {buildData.SecondMinimalMaximumHitTaken == 0 ? buildData.PhysicalMaximumHitTaken : buildData.SecondMinimalMaximumHitTaken}</p>
<p>Phy Reduction: {buildData.PhysicalDamageReduction}% Evasion: {buildData.MeleeEvadeChance}% Spell Supression: {buildData.SpellSuppressionChance}%</p>
<p>Resistances: <span className="redText">{buildData.FireResist}</span>/<span className="blueText">{buildData.ColdResist}</span>/<span className="yellowText">{buildData.LightningResist}</span>/<span className="purpleText">{buildData.ChaosResist}</span></p>
</div>
</ul>
))}
</div>
)}
</>
)
}
function loadPoBXml(str) {
var res = decodePoBString(str);
var xml = new XMLParser().parseFromString(res);
console.log("pob data was loaded")
return xml
}
function decodePoBString(str) {
return inflateSync(new Buffer(str, "base64")).toString()
}
I am new to react. I'm trying to update the parent state from the child but i have an error on another component at the the same level of the child one.
that's my code.
RedirectPage.js (parent)
const RedirectPage = (props) => {
const [status, setStatus] = useState("Loading");
const [weather, setWeather] = useState(null);
const [location, setLocation] = useState(null)
const [showLoader, setShowLoader] = useState(true)
const [userId, setUserId] = useState(false)
const [isPlaylistCreated, setIsPlaylistCreated] = useState(false)
const headers = getParamValues(props.location.hash)
const getWeather = () =>{
//fetch data..
//...
//...
.then(response => {
var res = response.json();
return res;
})
.then(result => {
setWeather(result)
setShowLoader(false)
setStatus(null)
setLocation(result.name)
});
})
}
const changeStateFromChild = (value) => {
setIsPlaylistCreated(value)
}
useEffect(() => {
getWeather()
},[]);
return (
<div className="containerRedirectPage">
{showLoader ? (
<div className="wrapperLogo">
<img src={loader}className="" alt="logo" />
</div>)
: (
<div className="wrapperColonne">
<div className="firstRow">
<WeatherCard weatherConditions={weather}/>
</div>
{isPlaylistCreated ? (
<div className="secondRow">
<PlaylistCard />
</div>
) : (
<PlaylistButton userId={userId} headers={headers} weatherInfo={weather} playlistCreated={changeStateFromChild} />
)}
</div>
)}
</div>
)
};
export default RedirectPage;
PlaylistButton.js:
export default function PlaylistButton({userId, headers, weatherInfo, playlistCreated}) {
const buttonClicked = async () => {
// ...some code...
playlistCreated(true)
}
return (
<div className="button-container-1">
<span className="mas">CREA PLAYLIST</span>
<button onClick={buttonClicked} id='work' type="button" name="Hover">CREA PLAYLIST</button>
</div>
)
}
and that's the other component i'm getting the error when i click on button.
WeatherCard.js:
const WeatherCard = ({weatherConditions}) => {
const [weather, setWeather] = useState(null);
const [icon, setIcon] = useState(null);
const getTheIcon = () => {
// code to get the right icon
}
setIcon(x)
}
useEffect(() => {
getTheIcon()
},[]);
return (
<div className="weatherCard">
<div className="headerCard">
<h2>{weatherConditions.name}</h2>
<h3>{Math.floor(weatherConditions.main.temp)}°C</h3>
</div>
<div className="bodyCard">
<h5>{weatherConditions.weather[0].description}</h5>
<img className="weatherIcon" src={icon} alt="aa" />
</div>
</div>
)
};
export default WeatherCard;
the first time i load the redirect page WeatherCard component is right. When i click the button i get this error:
error
Can someone explain me why ?
What is the effect of the setting playlistCreated(true) ?
Does it affects the weatherCondition object ?
If weatherCondition could be undefined at some point you need to check it before using its properties (name, main.temp, and weather)
Update:
The error clearly state that it cannot read name from weather because it's undefined. You have to check it before using the weather object properties.
if (!weatherConditions) {
return <div>Loading...</div> // or something appropriate.
}
return (
<div className="weatherCard">
<div className="headerCard">
<h2>{weatherConditions.name}</h2>
{weatherConditions.main && <h3>{Math.floor(weatherConditions.main.temp)}°C</h3>}
</div>
<div className="bodyCard">
{weatherConditions.weather &&
{weatherConditions.weather.length > 0 &&
<h5>{weatherConditions.weather[0].description}</h5>}
....
)
In this React code what I'm trying to do is getting all items from the list that match with what type in the text input that's in the setManName function (also there is one in setModeName function). It works, but when I delete the text input and start over, the items disappear and will not appear anymore, not showing on the screen unless I reload the page again and start over again. I am using inludes() method, which works fine, but once I delete a letter or whole word and start over again it doesn't work. What's the problem here? Should I be using a different approach? Like another useEffect or something?
import React, { useState, useEffect } from "react";
import "./style.css";
export default function App() {
const [items, setItems] = useState([])
const [openFilterCt, setOpenFilterCt] = useState(false)
const [term1, setTerm1] = useState()
const [term2, setTerm2] = useState()
useEffect(() => {
fetch("https://private-anon-af560a53c6-carsapi1.apiary-mock.com/cars")
.then(res => res.json())
.then(data => {
setItems(data)
})
}, [])
function setManName(e) {
setTerm1(e.target.value);
let u = items.filter(item => {
**return item.make.includes(e.target.value)**
})
setItems(u)
}
function setModName(e) {
setTerm2(e.target.value);
let u = items.filter(item => {
**return item.model.includes(e.target.value)**
})
setItems(u)
}
function hi() {
setOpenFilterCt(!openFilterCt)
}
return (
<div>
<h1>React Search & Filter</h1>
<div>
<h3 onClick={hi}>Filter</h3>
<div className={openFilterCt ? "show" : "hide"}>
<label>
Name of manufacturer: <input type="text" value={term1} onChange={setManName} />
</label>
<br />
<label>
Name of model: <input type="text" value={term2} onChange={setModName} />
</label>
</div>
</div>
{items.slice(0, 50).map((a, index) => {
return (
<div key={index} style={{border: "1px solid black", margin: "10px", padding: "5px"}}>
<p>Manufacturer: {a.make[0].toUpperCase() + a.make.slice(1)}</p>
<p>Model: {a.model}</p>
</div>
)
})}
</div>
);
}
You are overwriting the items object, so any items not in a search will not show up even after deleting characters. This solution will dynamically filter the items, rather than removing them from the array.
Additionally, you should provide a default value to the term1 and term2 states. Without a default value, the inputs are switching from uncontrolled to controlled inputs, a practice that is discouraged in React.
See this Codesandbox.
import React, { useState, useEffect } from "react";
import "./style.css";
export default function App() {
const [items, setItems] = useState([]);
const [openFilterCt, setOpenFilterCt] = useState(false);
const [term1, setTerm1] = useState("");
const [term2, setTerm2] = useState("");
useEffect(() => {
fetch("https://private-anon-af560a53c6-carsapi1.apiary-mock.com/cars")
.then((res) => res.json())
.then((data) => {
setItems(data);
});
}, []);
function setManName(e) {
setTerm1(e.target.value);
}
function setModName(e) {
setTerm2(e.target.value);
}
function filterItems(item) {
if (term1 && !item.make.includes(term1)) return false;
if (term2 && !item.model.includes(term2)) return false;
return true;
}
function hi() {
setOpenFilterCt(!openFilterCt);
}
return (
<div>
<h1>React Search & Filter</h1>
<div>
<h3 onClick={hi}>Filter</h3>
<div className={openFilterCt ? "show" : "hide"}>
<label>
Name of manufacturer:{" "}
<input type="text" value={term1} onInput={setManName} />
</label>
<br />
<label>
Name of model:{" "}
<input type="text" value={term2} onInput={setModName} />
</label>
</div>
</div>
{items
.filter(filterItems)
.slice(0, 50)
.map((a, index) => {
return (
<div
key={index}
style={{
border: "1px solid black",
margin: "10px",
padding: "5px"
}}
>
<p>Manufacturer: {a.make[0].toUpperCase() + a.make.slice(1)}</p>
<p>Model: {a.model}</p>
</div>
);
})}
</div>
);
}
I have just fixed it for your model search, you can do the same for the manufacturer search. There may be more optimal ways, but this is something I worked it out.
What you need to do is preserve your original list. .filter() actually changes the original list, and when the response is blank, the original data is gone. So I just preserved the old data
const [orItem, setOrItems] = useState([]);
const prevList = orItem;
function setModName(e) {
setTerm2(e.target.value);
let u = prevList.filter((item) => {
return item.model.includes(e.target.value);
});
setItems(u);
}
You can see you code in action here for model search:
https://codesandbox.io/s/elegant-mcclintock-46ncr?file=/src/App.js
First, you should not modify the original array items. You need to create another one(another state variable) filteredItems so you can reset to the original state, also I believe there is another error here item.model.includes(e.target.value), it will always return false if the text is empty.
function setManName(e) {
setTerm1(e.target.value);
if(e.target.value){
let u = items.filter(item => {
return item.make.includes(e.target.value)
})
setFilteredItems(u)
}else{
setItems(items)
}
}
Also useEffect hook should be something like this:
useEffect(() => {
fetch("https://private-anon-af560a53c6-carsapi1.apiary-mock.com/cars")
.then(res => res.json())
.then(data => {
setItems(data)
setFilteredItems(data)
})
}, [])
And make sure to map over filteredItems.
The problem here is that you are resetting the value that you receive from the given URL. You should be maintaining a separate list for visibility you could go by approach 1. given below. Its the best I could do without modifying lots of your code however this is typically an over/mis use of states. Remember React is called react because of its amazing capability to react when the state changes.
The Approach2 realizes just that, you can be smart with the filter and alter it as you need. your search to behave.
// Approach 1
import React, { useState, useEffect } from "react";
// import "./style.css";
export default function App() {
const [items, setItems] = useState([]);
const [visibleItems, setVisibleItems] = useState([]);
const [openFilterCt, setOpenFilterCt] = useState(false)
const [term1, setTerm1] = useState()
const [term2, setTerm2] = useState()
useEffect(() => {
fetch("https://private-anon-af560a53c6-carsapi1.apiary-mock.com/cars")
.then(res => res.json())
.then(data => {
setItems(data);
setVisibleItems(data);
})
}, [])
function setManName(e) {
// setTerm1(e.target.value);
let u = items.filter(item => {
if(e.target.value){
return item.make.includes(e.target.value)
}
return true;
})
setVisibleItems(u)
}
function setModName(e) {
// setTerm2(e.target.value);
let u = items.filter(item => {
return item.model.includes(e.target.value)
})
setVisibleItems(u)
}
function hi() {
setOpenFilterCt(!openFilterCt)
}
return (
<div>
<h1>React Search & Filter</h1>
<div>
<h3 onClick={hi}>Filter</h3>
<div className={openFilterCt ? "show" : "hide"}>
<label>
Name of manufacturer: <input type="text" value={term1} onChange={setManName} />
</label>
<br />
<label>
Name of model: <input type="text" value={term2} onChange={setModName} />
</label>
</div>
</div>
{visibleItems.slice(0, 50).map((a, index) => {
return (
<div key={index} style={{border: "1px solid black", margin: "10px", padding: "5px"}}>
<p>Manufacturer: {a.make[0].toUpperCase() + a.make.slice(1)}</p>
<p>Model: {a.model}</p>
</div>
)
})}
</div>
);
}
// Approach2
import React, { useState, useEffect } from "react";
// import "./style.css";
export default function App() {
const [items, setItems] = useState([]);
const [openFilterCt, setOpenFilterCt] = useState(false);
const [term1, setTerm1] = useState("");
const [term2, setTerm2] = useState("");
useEffect(() => {
fetch("https://private-anon-af560a53c6-carsapi1.apiary-mock.com/cars")
.then((res) => res.json())
.then((data) => {
setItems(data);
});
}, []);
function setManName(e) {
setTerm1(e.target.value);
}
function setModName(e) {
setTerm2(e.target.value);
}
function hi() {
setOpenFilterCt(!openFilterCt);
}
return (
<div>
<h1>React Search & Filter</h1>
<div>
<h3 onClick={hi}>Filter</h3>
<div className={openFilterCt ? "show" : "hide"}>
<label>
Name of manufacturer:{" "}
<input type="text" value={term1} onChange={setManName} />
</label>
<br />
<label>
Name of model:{" "}
<input type="text" value={term2} onChange={setModName} />
</label>
</div>
</div>
{items
.filter((item) => {
return item.make.includes(term1) && item.model.includes(term2);
})
.slice(0, 50)
.map((a, index) => {
return (
<div
key={index}
style={{
border: "1px solid black",
margin: "10px",
padding: "5px"
}}
>
<p>Manufacturer: {a.make[0].toUpperCase() + a.make.slice(1)}</p>
<p>Model: {a.model}</p>
</div>
);
})}
</div>
);
}
Ill be changing the key shortly. Using the code below I should be able to load a list of movies from the API and each movie should be linked to it's Provider Link website. using
the upMovieDetail. can anyone help point me in the right direction? I have a feeling it has something to do with the component being re-renderd after the click?
here is the codesandbox if you'd rather try to fix it here.. --
https://codesandbox.io/s/movieapp-searchbar-forked-qv1o6
const key ="fde5ddeba3b7dec3fc1f51852ca0fb95";
const getUpMovieDetail = (movieId) => {
//const [movieId, setMovieId] = useState([]);
const url = `https://api.themoviedb.org/3/movie/${movieId}/watch/providers?api_key=${key}`;
return fetch(url);
};
function UpMovieDetail({ movieItem }) {
const [searchLink, setSearchLink] = useState(null);
useEffect(() => {
getUpMovieDetail(movieItem.id)
.then((res) => res.json())
.then((res) => {
setSearchLink(res?.results?.US?.link);
});
}, [movieItem.id]);
return (
<ul className="flexed-search">
{searchLink.map((item) =>
<div className="poster-container" key={item.id}>
<li className="list-item">
<a target="_blank" rel="noopener noreferrer" href={searchLink}
onclick={((event) => {event.preventDefault()})}>
<img className="image-element" tabIndex="0" alt="movie poster"
title={`--Title: ${item.title}-- --Description:
${item.overview}-- --Vote Average: ${item.vote_average}`}
aria-label={item.title}
src={`https://image.tmdb.org/t/p/w500${item.poster_path}`} />
</a>
<h3 className="posterTitle">{item.title}</h3>
</li>
</div>
)}
</ul>
);
};
const SearchBar = () => {
const [search, setSearch] = useState([]);
const [input, setInput] = useState('');
// Input Field
const onUserInput = ({target}) => {
setInput(target.value);
};
// Api Call
const SearchApi = (event) => {
const aUrl = "https://api.themoviedb.org/3/search/movie?api_key=fde5ddeba3b7dec3fc1f51852ca0fb95";
const newUrl = aUrl +'&query=' + input;
event.preventDefault();
fetch(newUrl)
.then((response) => response.json())
.then((data) => {
setSearch(data.results);
})
.catch((error) => {
console.log('Error!! Data interupted!:', error)
})
};
return (
// Heading
<div>
<div className="container">
<h1>Movie Search Extravaganza!</h1>
{/* Input Field and Button Form */}
<form onSubmit={SearchApi}>
<input value={input} onChange={onUserInput} type="text" className="searchbar" aria-label="searchbar" placeholder="search" required/>
<br></br>
<button type="submit" aria-label="searchbutton" className="searchBtn">Movie Express Search</button>
</form>
<h1 className="row-label" tabIndex="0">Movies Related To Your Search</h1>
</div>
<div className="byName-container">
{search.map((item) => (
<UpMovieDetail key={item.id} movieItem={item} />
))}
</div>
</div>
)};
export default SearchBar;```
[1]: http://codesandbox.io/s/movieapp-searchbar-forked-qv1o6
[2]: https://codesandbox.io/s/movieapp-searchbar-forked-qv1o6
From the first render it throws the error because searchLink is null.
Try this:
{
searchLink && searchLink.length && searchLink.map((item) =>
...
}