Search does not work like expect in react - javascript

In my application i use a search input to search values, and select input also to filter values from my data. Now my component looks like below:
export default function App() {
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res= data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res= data.filter(i => i.age === Number(value));
setMydata(res);
}
return (
<div className="App">
<Select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Now, the functionality works. If you search something, appear results, and if you try to select a value, also appears the value that you selected.
Issue: If i select from dropdown, for example: 32, appears:
Julia is 32
Bill is 32
Bill is 32
And now if i want to search from the list above just Julia, i type Julia in search, it search from the whole list of data, not just from the list which i get after i selected 32. How to solve this, and how to get the result from the last results, not to search from the whole list, but from the last result?
Note: the same issue is when i search first and after that i select a value from dropdown.

Your two filters always work with the same object data, and not previously filtered state data myData. Best practice save value of filters in state and each render filter data:
export default function App() {
const [age, setAge] = useState('');
const [name, setName] = useState('');
const filteredData = data
.filter(i => Boolean(age) ? i.age === Number(age) : true)
.filter(i => i.name.toLowerCase().includes(name.toLowerCase()));
return (
<div className="App">
<Select value={age} style={{ width: 120 }} onChange={setAge}>
<Option value="2">2</Option>
<Option value="32">32</Option>
</Select>
<input value={name} onChange={e => setName(e.target.value)} />
<h1>Data</h1>
{filteredData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}

Try this one out:
import React, { useState } from "react";
export default function App() {
const data = [
{ age: 2, name: 'John' },
{ age: 32,name: 'Mark' },
{ age: 22,name: 'Dell' },
{ age: 14,name: 'Linda' },
{ age: 16,name: 'Jon' },
{ age: 18,name: 'Ron' }
];
const [myData, setMydata] = useState([]);
const searchData = e => {
const v = e.target.value;
const res = data.filter(i =>
i.name.toLowerCase().includes(v.toLowerCase())
);
setMydata(res);
};
function handleChange(value) {
const res = data.filter(i => i.age === Number(value.target.value));
console.log("This is the value and respos", value.target.value);
setMydata(res);
}
return (
<div>
<select defaultValue="" style={{ width: 120 }} onChange={handleChange}>
<option value="2">2</option>
<option value="32">32</option>
</select>
<input onChange={searchData} />
<h1>Data</h1>
{myData.map((i, k) => (
<div key={k}>
{i.name} is <span>{i.age}</span>
</div>
))}
</div>
);
}
Here is the codesandbox demo: Demo

Related

How to filter data after data is fetched in React?

I'm new in React. I'm try to filter data after this data will received.
I have select tag with options and I want to change data array after select will changed.
For example if selected option will have value 'crypto' show array only with type 'crypto'
Thanks for answer
import { useState, useEffect } from 'react'
const Fetch = () => {
const [data, setData] = useState();
const [currency, setCurrency] = useState('crypto');
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
fetch('https://api.coingecko.com/api/v3/exchange_rates')
.then((res) => { return res.json() })
.then((res) => { setData(res.rates); setIsLoaded(true); console.log(res); })
}, [])
if (!isLoaded) {
return <h2>Waiting for data...</h2>
} else {
return (
<div>
<label htmlFor="currency">Choose currency: </label>
<select id='currency' onChange={setCurrency}>
<option value="all">All</option>
<option value="crypto">Crypto</option>
<option value="fiat">Fiat</option>
<option value="commodity">Commodity</option>
</select>
{Object.values(data).map((item, i) => {
return (
<div key={i}>
<h3>{item.name}({item.unit}) : <span style={{ color: "red" }}>{item.value}</span></h3>
<p>type: {item.type} </p>
<hr />
</div>
)
})}
</div>
)
}
}
export default Fetch;
If statement not working
To filter data in JavaScript, you may use the filter function:
Object.values(data)
.filter(item => item?.type === currency)
.map(...)
Pass a function of your choice that returns either true or false for each value in the array.
If you want more details, you can find them in the docs.
First you need to fix your onChange handler this returns a React ChangeEvent. Currently you're setting the state to the event instead of the selected value. For this you'll also need to add a value prop to make the select element a controlled input.
<select
id="currency"
onChange={() => setCurrency(e.target.value)}
value={currency}
>
...
</select>
For the filtering you can use filter.
{
Object.values(data)
.filter((item) => item.type === currency)
.map((item, i) => {
return (
<div key={i}>
<h3>
{item.name}({item.unit}) :{" "}
<span style={{ color: "red" }}>{item.value}</span>
</h3>
<p>type: {item.type} </p>
<hr />
</div>
);
});
}

How to capture value selected from a dropdown list?

I have a simple form that I am creating using react JS. I have 2 text fields, Type & Year, and one dropdown which is car. The car dropdown is populated using a value coming from an API. The user enters text value for the first 2 fields and selects the value for car from the dropdown menu and hits add. I have another post API that captures this value. I can store and set the value for the text boxes but for the dropdown, the value of car is not getting captured and I get a null value. How can I store the value of car like I am doing for other 2 text boxes
import { useHistory } from "react-router-dom";
import axios from "axios";
import React,{useState,useEffect} from 'react';
function NewCar() {
const [type, setType] = useState("");
const [year, setYear] = useState("");
const [car, setCar] = useState("");
let history = useHistory();
const [entries,setEntries]=useState([])
useEffect(() => {
fetchValues();
}, [])
useEffect(() => {
console.log(entries)
}, [entries])
const fetchValues = async()=>{
const response = await axios('http://localhost:8080/cars');
setEntries(response.data)
}
const onSubmit = function (e) {
e.preventDefault();
axios
.post("http://localhost:8080/update", {
type,
year,
car
})
.then(() => history.push("/"));
};
return (
<form onSubmit={onSubmit}>
<table border="1">
Type:{" "}
<input
name="type"
value={type}
onChange={(e) => setType(e.target.value)}
/>
<br />
Year:{" "}
<textarea
name="year"
value={year}
onChange={(e) => setYear(e.target.value)}
></textarea>{" "}
<br />
<label for="car">Car:</label>
<select id="car" name="car">
{entries.map((entry) => (
<option value={entry.name}>{entry.name}</option>
))}
name="car"
value={car}
onChange={(e) => setCar(e.target.value)}
</select>
<br />
</table>
<button>Add</button>
</form>
);
}
export default NewCar;
I believe you made a mistake in your code by placing your attributes outside the select tag, it should look something like this:
<select
id="car"
name="car"
value={car}
onChange={(e) => setCar(e.target.value)}
>
{entries.map((entry) => (
<option value={entry.name}>{entry.name}</option>
))}
</select>
I have this sample in which u can get the the value of a select tag or dropdown, hope you can reference from it.
const [dataItem, setDataItem] = useState("");
const data = [
{
name: "Ford",
year: "1903",
},
{
name: "Chevy",
year: "1904",
},
{
name: "Dodge",
year: "1905",
},
{
name: "Honda",
year: "1906",
},
{
name: "Toyota",
year: "1907",
},
];
return (
<>
{dataItem}
<select
name="dataItem"
value={dataItem}
onChange={(e) => setDataItem(e.target.value)}
>
{data.map(({name}) => (
<option value={name}>{name}</option>
))}
</select>
</>
);
If it still went wrong, please let me know and write a comment below.

Fetch the value of props other than "value" from a custom component?

So I have the below code:
function Crafting(props) {
const craftItems = [
{
key: 1,
name: "Bronze sword",
},
{
key: 2,
name: "Iron sword",
},
{
key: 3,
name: "Steel sword",
},
];
const [item, setItem] = useState();
const [itemKey, setItemKey] = useState();
function onChangeHandler(event) {
setItem(event.target.value);
setItemKey(event.target.itemID);
console.log(event);
}
function onSubmitHandler(event) {
event.preventDefault();
console.log(item);
console.log(itemKey);
}
return (
<Card className={classes.renderwin}>
<form onSubmit={onSubmitHandler}>
<label htmlFor="items">Select an item</label>
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} itemID={item.key}> {item.name} </option>
))}
</select>
<Button type="submit">Craft item</Button>
</form>
</Card>
);
}
I want to be able to retrieve the original key value against the item. I tried using "key", then added in a custom prop called "itemID" but they both just return as undefined. How do I fetch the ID back based on the value selection?
The issue is that you can basically store only an option value and retrieve that in the handler to save into state.
I would place the item.key on the option element's value attribute.
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} value={item.key}>
{item.name}
</option>
))}
</select>
Access the onChange event object's value property in the handler and convert it back to a Number type before storing in state.
function onChangeHandler(event) {
const { value } = event.target;
setItemKey(Number(value));
}
When accessing in the submit handler use the stored itemKey to search the craftItems array for the matching object.
function onSubmitHandler(event) {
event.preventDefault();
const item = craftItems.find(({ key }) => key === itemKey);
console.log(item?.name);
console.log(item?.key);
}
function Crafting(props) {
const craftItems = [
{
key: 1,
name: "Bronze sword"
},
{
key: 2,
name: "Iron sword"
},
{
key: 3,
name: "Steel sword"
}
];
const [itemKey, setItemKey] = React.useState();
function onChangeHandler(event) {
const { value } = event.target;
setItemKey(Number(value));
console.log({ value });
}
function onSubmitHandler(event) {
event.preventDefault();
const item = craftItems.find(({ key }) => key === itemKey);
console.log(item && item.name);
console.log(item && item.key);
}
return (
// <Card className={classes.renderwin}>
<form onSubmit={onSubmitHandler}>
<label htmlFor="items">Select an item</label>
<select name="items" onChange={onChangeHandler}>
{craftItems.map((item) => (
<option key={item.key} value={item.key}>
{item.name}
</option>
))}
</select>
<button type="submit">Craft item</button>
</form>
// </Card>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Crafting />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
Place a value on the option like this:
{craftItems.map((item) => (
<option value={item.key} key={item.key}>{item.name}</option>
))}

How to get individual items to display on a page?

database
I have an interesting bug in my code that I can`t figure out. It should be a simple React+Firestore setup, listing items on one page, and showing more details on each item on the next. Unfortunately, it only shows details for the first item on the list.
Have been digging through the Firestore documentation, where I found the following solution. It's not working...
Details
import React, { useState, useEffect } from "react";
import firebase from "./firebase";
import Servis from "./funkc/servisni";
export default function FireDetail({ match }) {
// console.log(match);
console.log(match.params.id);
const [item, setItem] = useState([]);
const [loading, setLoading] = useState(true);
const getIt = () => {
setLoading(true);
const item = [];
const docRef = firebase
.firestore()
.collection("polja")
.doc("id", "==", match.params.id);
docRef.onSnapshot((doc) => {
if (doc.exists) {
console.log("Document data:", doc.data());
setItem(doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
});
setLoading(false);
};
useEffect(() => {
getIt();
}, [match]);
if (loading) {
return <h3>samo malo...</h3>;
}
return (
<div className="container">
<div>
Kontakt: tip - email
<p> {item.Kontakt} </p>
</div>
<div>
<p>Datum rodjenja: {item.Datum}</p>
{item.Prezime} {item.Ime}
</div>
</div>
);
}
List
the component that lists all of the items in the database...
const SORTER = {
"Prezime A-Z": { column: "Prezime", direction: "asc" },
"Prezime Z-A": { column: "Prezime", direction: "desc" },
"Email A-Z": { column: "Kontakt", direction: "asc" },
};
const PAGER = {
5: { Max: "5" },
30: { Max: "30" },
45: { Max: "45" },
};
export default function FireList() {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [sortBy, setSortBy] = useState("Prezime A-Z");
const [displayMax, setDisplayMax] = useState("5");
const [query, setQuery] = useState("");
// function routeTo() {
// const { id } = useParams();
// }
const ref = firebase
.firestore()
.collection("polja")
.orderBy(SORTER[sortBy].column, SORTER[sortBy].direction)
.limitToLast(PAGER[displayMax].column);
// console.log(ref);
function getEm() {
setLoading(true);
ref.get().then((querySnapshot) => {
const items = [];
querySnapshot.forEach((doc) => {
const item = {
...doc.data(),
id: doc.id,
};
items.push(item);
});
setItems(items);
// console.log(items);
setLoading(false);
});
}
useEffect(() => {
getEm();
}, [query, sortBy, displayMax]);
return (
<div>
<div>
{" "}
<label>Poredaj</label>
<select
value={sortBy}
onChange={(e) => setSortBy(e.currentTarget.value)}
>
<option value="Prezime A-Z"> Prezime A-Z </option>
<option value="Prezime Z-A"> Prezime Z-A </option>
<option value="Email A-Z"> Email A-Z </option>
</select>
</div>
<div>
<label> Max. po stranici </label>
<select
value={displayMax}
onChange={(e) => setDisplayMax(e.currentTarget.value)}
>
<option value="5">5</option>
<option value="30">30</option>
<option value="45">45</option>
</select>
</div>
<ul>
<input
type="text"
value={query}
onChange={(event) => setQuery(event.target.value)}
></input>
</ul>
{loading ? <h1>Loading...</h1> : null}
{items.map((val) => (
<div key={val.id}>
<p>
{val.Ime} {val.Prezime}
<Link to={`/kontakt/detalji/${val.id}`}> ajd </Link>
</p>
</div>
))}
</div>
);
}
App component, providing navigation
return (
<BrowserRouter>
<div className="App">
login
<div>
<Header />
</div>
<Route path="/" exact component={Header} />
<Route path="/adresar" component={FireList} />
<Route path="/kontakt" exact component={ContactEdit} />
<Route path="/kontakt/detalji/:id" component={FireDetail} />
</div>
</BrowserRouter>
);
}
export default App;
I think you must read the url parameter on each render. Thath meanes useEffect( yourfunction, [match]) (avoid using [ ] as second parameter this time)
Remember, ReactRouter doesnt reload or load a page when route somwhere. All is in memory, except the first time.

How can I convert the this cascading dropdown class component to functional one using React Hooks?

I have this cascading dropdown in class component form and it is working fine but I want to convert it to functional one with React Hooks. It is a Country, State, City dropdown. When a user select country, the states under it will load. When they select a state, the cities under it will load. I have changed the constructor part to start using state as seen below. Also, the componentDidMount has been converted to useEffect. The handlers have also been converted.
But the render part below where the form is displayed is giving me tough time. When I tried, I was getting the error that it is not properly arranged. How can I convert the part inside the container div from using this. to be in sync with the rest of the function. I know I'll have to do away with this. but I don't know how to go about it? The sample function is posted below.
function LocationDropdown() {
const [state, setState] = useState({
countries : [],
states : [],
lgas : [],
selectedCountry : '--Choose Country--',
selectedState : '--Choose State--'
});
useEffect(() => {
setState(prevState => ({
...prevState,
countries : [
{ name: 'Nigeria', value: 'nigeria',
states: [ {name: 'Abia', value: 'abia',
lgas: [
{name: "Aba", value: 'aba'},
{name: "Oru", value: 'oru'},
]}, {name: 'Adamawa', value: 'adamawa',
lgas: [
{name: 'Demsa', value: 'demsa'},
{name: 'Fufure', value: 'fufure'},
]},
},
]
}));
}, [])
changeCountry(event) {
this.setState({selectedCountry: event.target.value});
this.setState({states : this.state.countries.find(cntry => cntry.name === event.target.value).states});
}
changeState(event) {
this.setState({selectedState: event.target.value});
const stats = this.state.countries.find(cntry => cntry.name === this.state.selectedCountry).states;
this.setState({lgas : stats.find(stats => stats.name === event.target.value).lgas});
}
render() {
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<Label>Country</Label>
<Select placeholder="Country" value={this.state.selectedCountry} onChange={this.changeCountry}>
<option>--Choose Country--</option>
{this.state.countries.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</Select>
</div>
<div>
<Label>State</Label>
<Select placeholder="State" value={this.state.selectedState} onChange={this.changeState}>
<option>--Choose State--</option>
{this.state.states.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</Select>
</div>
<div>
<Label>LGA</Label>
<Select placeholder="LGA" value={this.state.selectedLga}>
<option>--Choose LGA--</option>
{this.state.lgas.map((e, key) => {
return <option key={key}>{e.name}</option>;
})}
</Select>
</div>
</div>
)
}
}
export default LocationDropdown;
I changed your code. I renamed state and setState, so it's not confusing with actual States. Also I changed Select and Label to normal html element so i can test.
import React, { useEffect, useState } from "react";
function LocationDropdown() {
const [myData, setMyData] = useState({
countries: [],
states: [],
lgas: [],
selectedCountry: "--Choose Country--",
selectedState: "--Choose State--"
});
useEffect(() => {
setMyData(prevState => ({
...prevState,
countries: [
{
name: "Nigeria",
value: "nigeria",
states: [
{
name: "Abia",
value: "abia",
lgas: [
{ name: "Aba", value: "aba" },
{ name: "Oru", value: "oru" }
]
},
{
name: "Adamawa",
value: "adamawa",
lgas: [
{ name: "Demsa", value: "demsa" },
{ name: "Fufure", value: "fufure" }
]
}
]
}
]
}));
}, []);
const mergeAndUpdateMyData = newData => {
setMyData({ ...myData, ...newData });
};
const changeCountry = event => {
mergeAndUpdateMyData({
selectedCountry: event.target.value,
states: myData.countries.find(cntry => cntry.name === event.target.value)
.states
});
};
const changeState = event => {
const stats = myData.countries.find(
cntry => cntry.name === myData.selectedCountry
).states;
mergeAndUpdateMyData({
selectedState: event.target.value,
lgas: stats.find(stats => stats.name === event.target.value).lgas
});
};
return (
<div id="container">
<h2>Cascading or Dependent Dropdown using React</h2>
<div>
<label>Country</label>
<select
placeholder="Country"
value={myData.selectedCountry}
onChange={changeCountry}
>
<option>--Choose Country--</option>
{myData.countries.map((country, key) => {
return (
<option value={country.name} key={key}>
{country.name}
</option>
);
})}
</select>
</div>
<div>
<label>State</label>
<select
placeholder="State"
value={myData.selectedState}
onChange={changeState}
>
<option>--Choose State--</option>
{myData.states.map((state, key) => {
return (
<option value={state.name} key={key}>
{state.name}
</option>
);
})}
</select>
</div>
<div>
<label>LGA</label>
<select placeholder="LGA" value={myData.selectedLga}>
<option>--Choose LGA--</option>
{myData.lgas.map((lga, key) => {
return (
<option value={lga.name} key={key}>
{lga.name}
</option>
);
})}
</select>
</div>
</div>
);
}
export default LocationDropdown;
EDITED: Please note that in React, when you set a state, the statement is async. This means when you call setMyData, the value of myData is not updated immediately. So you cannot call mergeAndUpdateMyData multiple times in a row.
By the way, you can use multiple useState in one function components. For example:
const [countries, setCountries] = useState();
const [lgas, setLags] = useState();
...

Categories

Resources