I added a TextField from the MUI library, and used a useRef hook to capture the value "live" as the user types something. The intention is to filter only the rates which include the characters he types. As of right now:
Object.keys(rates["rates"]) // ["EUR", "RON", "CZK", ...]
I added a form, and I want it to stay persistent, but the buttons should change dynamically. If the user has not typed anything I want to return everything (like nothing is filtered)
My try:
import React, {useEffect, useRef, useState} from 'react'
import Button from '@mui/material/Button';
import CircularProgress from '@mui/material/CircularProgress';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
const RatesButton = () => {
const rateRef = useRef('')
const [rates, setRates] = useState([]);
useEffect(
() => {
fetch("https://api.vatcomply.com/rates")
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
, [])
if (rates.length === 0) {
return (
<Box sx={{display: 'flex', justifyContent: 'center'}}>
<CircularProgress/>
</Box>
)
}
const rateSearch = () => {
Object.keys(rates["rates"]).filter(
rate => rate.includes(rateRef.current.value)
).map(rate => {
return (
<Button>
{rate}
</Button>
)
}
)
}
return (
<>
<br/>
<TextField id="rate-search" onChange={rateSearch} inputRef={rateRef} label="Rate" variant="outlined"/>
</>
)
}
export default RatesButton
It works nicely I think, I can access the reference of the input of the user, filter all the rates that contain the letters, and map each one to a MUI Button. The problem is that they don't show somehow, and I am pretty lost, it is pretty confusing how I can return from two different functions at the same time, while keeping one persistent (the input field)

The buttons do not show unfortunately...
You should use controlled mode and store your TextField's value in a state using useState instead of useRef because changing the ref value doesn't trigger a re-render so the UI doesn't get updated. There are a lot of other wrong things in your code, I've fixed it all, feel free to ask me if you don't understand anything:
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={(e) => setValue(e.target.value)}
// inputRef={value}
label="Rate"
variant="outlined"
/>
</>
);
};
import React, {useState} from 'react';
import { throttle } from 'lodash';
const RatesButton = () => {
const [value, setValue] = useState("");
const [rates, setRates] = useState({});
useEffect(() => {
fetch("https://api.vatcomply.com/rates")
.then((ratesResponse) => ratesResponse.json())
.then((rates) => setRates(rates.rates ?? {}));
}, []);
const handleChange = (e) => setValue(e.target.value);
// it will prevent multiple render during fast typing
const throttledChange = throttle(handleChange, 400);
return (
<>
{Object.keys(rates).length === 0 && (
<Box sx={{ display: "flex", justifyContent: "center" }}>
<CircularProgress />
</Box>
)}
{Object.keys(rates)
.filter((rate) => rate.toLowerCase().includes(value.toLowerCase()))
.map((rate) => {
return <Button>{rate}</Button>;
})}
<br />
<TextField
id="rate-search"
onChange={throttledChange} // don't use arrow function
label="Rate"
variant="outlined"
/>
</>
);
};