I can succesfully get the data from my database , but how can I display it Inside the Select?
This is where i want to display my data inside the select
<Select
fullWidth
value={newUsers}
onChange={handleChange}
>
<MenuItem value={0}>{newUsers.label}</MenuItem>
this is how i call the data from the database (this is working and I can see the data from my console)
const [newUserLists, setNewUserLists] =useState([]);
const newUsers= [];
const fetchData = async () => {
const response = await getreason();
response.data.map(function(u){
newUsers.push({
label: u.applicationcode,
value: u.applicationid
})
})
setNewUserLists(newUsers)
}
useEffect(() => {
fetchData();
}, [reRender]);
this is the result in console.log
Let's model this on the Material-UI Select tutorial and API doc page. There, we can see a few things:
value is the currently selected input value. It appears you're trying to set it to the list of objects that you read from the DB response, which is not correct. It is common practice to say something like the following:
const [selected, setSelected] = useState("");
const handleChange = (event) => {
setSelected(event.target.value);
};
return (
<FormControl>
<InputLabel>Selection</InputLabel>
<Select
value={selected}
onChange={handleChange}
>
{/* MenuItems go here */}
</Select>
</FormControl>
);
On an unrelated (to Material-UI) note, you seem to not actually use newUserLists in your code. I'm going to make the assumption that newUsers is just a placeholder to munge the data from the response into, but per #Naim-Mustafa's answer, it isn't needed and should not be used in your display.
Now, how do we use newUsersList to generate a list of MenuItems? Just as you've done elsewhere, map is the key:
{newUsersList.map(({ label, value }) => (
<MenuItem id={value} value={value}>
{label}
</MenuItem>
))}
Note that I'm assuming value is unique for the purposes of the id field. If it is not, replace with something more appropriate.
You can change const newUsers= []; to let newUsers= [];
but you dont have to use newUsers array at all, instead try using the state
const [newUserLists, setNewUserLists] =useState([]);
const [selectedOption, setSelectedOption] = useState();
const fetchData = async () => {
const response = await getreason();
setNewUserLists(response.data.map(user => ({
label: user.applicationcode,
value: user.applicationid})))
}
useEffect(() => {
fetchData();
}, [reRender]);
return (
<>
<select value={selectedOption}
onChange={(event) => setSelectedOption(event.target.value)}
>
{newUserLists
.map(user =>
<option key={user.label} value={user.label}>
{user.label}</option>}
</select>
</>
Related
I understand destructuring that it unpacks values from array into variables and using square brackets in useState means it is returning two values in that array, first one is value and second one is function that is used to change that value,
But I see [] when using custom hook, and It does not make any sense to me , Can anyone please explain me why variable [breeds] has square brackets around it. it is declared under useState.
const ANIMALS = ["bird", "cat", "dog", "rabbit", "reptile"];
export const SearchParams = () => {
const [location, setLocation] = useState("");
const [animal, setAnimal] = useState('');
const [breed, setBreed] = useState('')
const [pets, setPets] = useState([]);
const [breeds] = useBreedList(animal);
useEffect(() => {
requestPets();
},[animal])
async function requestPets() {
const res = await fetch(
`http://pets-v2.dev-apis.com/pets?animal=${animal}&location=${location}&breed=${breed}`
);
const json = await res.json();
console.info('json is '+JSON.stringify(json))
setPets(json.pets);
console.warn("pets are " + JSON.stringify(pets));
}
return (
<div className="search-params">
<form>
<label htmlFor="location">
Location
<input
id="location"
value={location}
placeholder="Location"
onChange={(e) => setLocation(e.target.value)}
/>
</label>
<label htmlFor="animal">
Animal
<select
id="animal"
value={animal}
onChange={(e) => {
setAnimal(e.target.value);
setBreed("");
}}
onBlur={(e) => {
setAnimal(e.target.value);
setBreed("");
}}
>
<option />
{ANIMALS.map((animal) => (
<option key={animal}>{animal}</option>
))}
</select>
</label>
<label htmlFor="breed">
Breed
<select
id="breed"
value={breed}
onChange={(e) => setBreed(e.target.value)}
disabled={breeds.length === 0}
>
<option />
{breeds.map((breed) => (
<option key={breed}>{breed}</option>
))}
</select>
</label>
<button>Submit</button>
</form>
{pets.map((pet) => (
<Pet
name={pet.name}
animal={pet.animal}
breed={pet.breed}
key={pet.id}
/>
))}
</div>
);
}
This is custom hook
const localCache = {};
export const useBreedList = (animal) => {
const [breedList, setBreedList] = useState([]);
const [status, setStatus] = useState("unloaded");
useEffect(() => {
if (!animal) {
setBreedList([])
} else if (localCache[animal]) {
setBreedList(localCache[animal])
} else {
requestBreedList();
}
async function requestBreedList() {
setBreedList([]);
setStatus("loading");
const res = await fetch(
`https://pets-v2.dev-apis.com/breeds?animal=${animal}`
)
const json = await res.json();
localCache[animal] = json.breeds || []
setBreedList(localCache[animal]);
setStatus("loaded");
}
}, [animal]);
return [breedList, status];
}
Can anyone please explain me why variable [breeds] has square brackets around it. it is declared under useState.
Because it's destructuring the result of calling useBreedList, which (like useState) returns two pieces of information, wrapped up in a tuple (a fixed-length array):
export const useBreedList = (animal) => {
// ...
return [breedList, status]; // <=================================
};
Since they're returning two pieces of information, the people writing useBreedList had the same choice that the people writing useState did:
Return a tuple (fixed-length array) people can destructure into variables they control the names of
Return an object with (say) breedlist and status properties
It's easier to control your own variable names when using the hook when the hook uses a tuple (#1) rather than an object (#2) — and indeed, the code you've quoted is using breeds rather than breedList. If the hook did this:
return { breedList, status };
...then the quoted code would have had to do this to use the name breeds instead:
const { breedList: breeds } = useBreedList(animal);
That said, this hook is really specific, I wouldn't have been surprised to find it written using a (non-array) object instead, and for code using it to consistently use const { breedList, status } = useBreedList(animal); instead.
JavaScript doesn’t support functions that return multiple values. However, you can wrap multiple values into an array or an object and return the array or the object.
Use destructuring assignment syntax to unpack values from the array, or properties from objects.
I have a dropdown made with react-select, with multiple options that i get from an api. The code is working, when clicked the dropdown show the options, but when I select one option it stop showing the other ones. I don't what could it be since the isMulti prop is on.
Here is the code:
export const DropDown = ({ itemsOption, placeholder }) => {
const [options, setOptions] = useState([]);
const loadOptions = (op) => {
api.get(`${op}`).then(({ data }) => {
setOptions(
data.map((item) => {
return {
key: item.code,
label: item.name_ptbr,
};
})
);
});
};
useEffect(() => {
loadOptions(itemsOption);
}, []);
return (
<>
<DropStyled>
<Select
isMulti
options={options}
name={placeholder}
placeholder={placeholder}
closeMenuOnSelect={false}
/>
</DropStyled>
</>
);
};
An option needs a value property for react-select to map the data to its options.
So when mapping over the fetched data, add value along with label and key.
return {
key: item.code,
label: item.name_ptbr,
value: item.name_ptbr,
};
I have a page where I display the data from the API based on an id. I am using React Query to manage the storage of the data. What I am trying to do is when the input with the id is changed I'd like to refetch the data for a different object. I tried to do the following:
const useData = (id: string) => useQuery(
['data', id],
() => axios.get(`/api/data/${id}`),
{
enabled: !!id,
},
);
const Page = () => {
const [id, setID] = useState('1');
const [result, setResult] = useState(useData(id));
useEffect(() => {
setResult(useData(id));
}, [id]);
return (
<div>
{result.data}
<input onChange={(e) => setID(e.target.value)} />
</div>
);
};
But you cannot call hooks inside the useEffect callback. What would be the correct approach for me to reset the result with the data from a new API call?
react-query will automatically refetch if parts of the query key change. So you are on the right track regarding your custom hook, and for your App, it also becomes much simpler:
const useData = (id: string) => useQuery(
['data', id],
() => axios.get(`/api/data/${id}`),
{
enabled: !!id,
},
);
const Page = () => {
const [id, setID] = useState('1');
const result = useData(id);
return (
<div>
{result.data}
<input onChange={(e) => setID(e.target.value)} />
</div>
);
};
that's it. that's all you need.
if id changes, the query key changes, thus giving you a new cache entry, which react-query will fetch for you.
I'm pretty new to using hooks and functional components.
I have a Filtered List. When I try to update the filter, it will use the last filter state instead of the new one. I must be missing some render/state change orders, but I can't seem to figure out what it is.
I appreciate any help I can get :)
Pseudo code below:
export default function TransferList(props) {
const [wholeList, setWholeList] = React.useState([]);
const [filteredList, setFilteredList] = React.useState([]);
const [filter, setFilter] = React.useState([]);
return (
<>
<TextField
value={filter}
onChange={(e) => {
// Set filter input
setFilter(e.target.value);
// Filter the list
let filtered = wholeList.filter(
(item) => item.indexOf(filter) !== -1
);
setFilteredList(filtered);
}}
/>
<List>
{filteredList.map((item) => (
<ListItem>Item: {item}</ListItem>
))}
</List>
</>
);
}
Inside onChange you should use save the value in a constant, filter will not update just after setFilter(filterValue) as this is an async operation.
<TextField
value={filter}
onChange={e => {
const filterValue = e.target.value;
// Set filter input
setFilter(filterValue);
// Filter the list
let filtered = wholeList.filter(item => item.indexOf(filterValue) !== -1);
setFilteredList(filtered);
}}
/>;
State updates are asynchronous and hence the filter state update doesn't reflect immediately afterwords
You must store the new filter values and set the states based on that
export default function TransferList(props) {
const [wholeList, setWholeList] = React.useState([]);
const [filteredList, setFilteredList] = React.useState([]);
const [filter, setFilter] = React.useState([]);
return (
<>
<TextField value={filter} onChange={(e) => {
// Set filter input
const newFilter = e.target.value;
setFilter(newFilter)
// Filter the list
let filtered = wholeList.filter(item => item.indexOf(newFilter) !== -1)
setFilteredList(filtered)
}} />
<List>
{filteredList.map(item => <ListItem>Item: {item}</ListItem>)}
</List>
</>
)
}
So it turns out I had to give the initial filtered list the entire unfiltered list first. For some reason that fixes it.
const [filteredList, setFilteredList] = React.useState(props.wholeList);
I initially wanted an empty filter to display nothing, but I may have to settle for showing the entire list when the filter is empty
Order your code to be increase readability
In clean code
Main changes:
Use destructuring instead of props
Out the jsx from the html return to increase readability
Use includes instead of indexOf
Add key to the list
export default function TransferList({ wholeList }) {
const [filteredList, setFilteredList] = React.useState(wholeList);
const [filter, setFilter] = React.useState([]);
const handleOnChange = ({ target }) => {
setFilter(target.value);
const updatedFilteredList = wholeList.filter(item => item.includes(target.value));
setFilteredList(updatedFilteredList);
}
const list = filteredList.map(item => {
return <ListItem key={item}>Item: {item}</ListItem>
});
return (
<>
<TextField value={filter} onChange={handleOnChange} />
<List>{list}</List>
</>
);
}
and I have a filter component the do the filter list inside.
import React, { useState } from 'react';
function FilterInput({ list = [], filterKeys = [], placeholder = 'Search',
onFilter }) {
const [filterValue, setFilterValue] = useState('');
const updateFilterValue = ev => {
setFilterValue(ev.target.value);
const value = ev.target.value.toLowerCase();
if (!value) onFilter(list);
else {
const filteredList = list.filter(item => filterKeys.some(key => item[key].toLowerCase().includes(value)));
onFilter(filteredList);
}
}
return (
<input className="filter-input" type="text" placeholder={placeholder}
value={filterValue} onChange={updateFilterValue} />
);
}
export default FilterInput;
the call from the father component look like this
<FilterInput list={countries} filterKeys={['name']} placeholder="Search Country"
onFilter={filterCountries} />
this is in my corona app.. you can look on my Github.
and see how I build the filter
(https://github.com/omergal99/hello-corona)
I'm studying the reaction, and I'm trying to filter by manufacturer the products that I previously request via the API. With this code, products are displayed, and then you can filter by manufacturer. However, when you try to filter them again, i.e. select products from a different manufacturer, nothing happens.
My code:
import React, {useEffect} from 'react'
import Loader from './../Loader/Loader'
import ProductItem from './ProductItem'
function Catalog() {
const [products, setProducts] = React.useState([])
const [brands, setBrands] = React.useState([])
const [brand, setBrand] = React.useState('')
useEffect(() => {
const proxyUrl = 'https://cors-anywhere.herokuapp.com/'
const url = 'https://avtodoka-msk.ru/aimylogic-mission.json'
fetch(proxyUrl + url)
.then(response => response.json())
.then(products => {
setProducts(products)
const brands = []
products.map(product => {
return(
brands.push(...product.brend)
)
})
setBrands([...new Set(brands)])
})
}, [])
function toggleBrand(e) {
setBrand(e.target.value)
setProducts(
products.filter(product => {
if ([...product.brend].includes(e.target.value)) {
return true
}
})
)
}
return (
<div className="container pt-5">
{brands.length ? (
<div className="row">
<div className="col-3">
<select value={brand} onChange={toggleBrand}>
<option value={''}>Выбрать бренд</option>
{brands.map((brend,index) => {
return(
<option value={brend} key={index}>{brend}</option>
)
})}
</select>
</div>
</div>
): null}
{products.length ? (
<div className="row">
{products.map(product => {
return (
<ProductItem
key={product.id}
product={product}
/>
)
})}
</div>
) : <Loader />}
</div>
)
}
export default Catalog
If I understood correctly your state needs to have:
products array - containing your products.
brands array - containing your manufacturers.
brand array - containing an active filter by manufacturer.
If that is correct, then I see a couple of problems with your code:
You're overcomplicating your array methods, leading to wanted behaviors.
Now, the real problem here is that when you filter the first time, you lose access to all the products, and therefore you need to re-fetch them on every filter click.
Giving that, here's your updated code:
import React, {useEffect} from 'react'
import Loader from './../Loader/Loader'
import ProductItem from './ProductItem'
function Catalog() {
const [products, setProducts] = React.useState([])
const [brands, setBrands] = React.useState([])
const [brand, setBrand] = React.useState('')
useEffect(() => {
const proxyUrl = 'https://cors-anywhere.herokuapp.com/'
const url = 'https://avtodoka-msk.ru/aimylogic-mission.json'
fetch(proxyUrl + URL)
.then(response => response.json())
.then(products => {
if (brands.length <= 0)
setBrandsFromProducs(products)
const filteredProducts = products.filter(product => !brand || product.brend.includes(brand))
setProducts(filteredProducts)
})
}, [brand])
function setBrandsFromProducs(products) {
const brands = products.reduce(
(acc, product) => ([...acc, ...product.brend])
, [])
setBrands([...new Set(brands)])
}
function toggleBrand(e) {
const selectedBrand = e.target.value
setBrand(selectedBrand)
}
}
Key aspects:
product.brend is an array. To extract all the brands from this JSON, you need to flatten an array of arrays, therefore you're reducing it into a flat array - hence the usage of a .reduce(). You only need to do this if the brands array is empty. However, if you're not concerned about performance you can remove the if statement to always reset the brandsarray.
Your event is selecting the brand, it shouldn't contain any other side-effect (filtering the products). Those should live inside a proper scope -> useEffect. To trigger this event, you need to 'listen' to changes to the brand filter, therefore it needs to be specified as a dependency of the useEffect.
Your filter was missing the return false outside the if statement. However, it could be simplified because .includes() returns a boolean value and doesn't mutate the original array. The filter I created will first check if you have an active filter and only filter if you do have one.
You need to make sure that e.target.value is the exact string from the product.brend's array.
Small suggestions:
If you control the API, your filters should always be on the backend side to avoid bigger payloads than you need.
If you don't use the brand state, you can safely remove it. Without your JSX I can't be sure.