Agregué un campo de TextField de la biblioteca MUI y usé un gancho useRef para capturar el valor "en vivo" cuando el usuario escribe algo. La intención es filtrar solo las tarifas que incluyen los caracteres que escribe. A partir de ahora:
Object.keys(rates["rates"]) // ["EUR", "RON", "CZK", ...]Agregué un formulario y quiero que permanezca persistente, pero los botones deberían cambiar dinámicamente. Si el usuario no ha escrito nada, quiero devolver todo (como si no se filtrara nada)
Mi intento:
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 Creo que funciona bien, puedo acceder a la referencia de la entrada del usuario, filtrar todas las tarifas que contienen las letras y asignar cada una a un Button MUI. El problema es que no se muestran de alguna manera, y estoy bastante perdido, es bastante confuso cómo puedo regresar de dos funciones diferentes al mismo tiempo, mientras mantengo una persistente (el campo de entrada)

Los botones no se muestran lamentablemente...
Debe usar el modo controlado y almacenar el valor de su TextField en un estado usando useState en lugar de useRef porque cambiar el valor de referencia no desencadena una nueva representación, por lo que la interfaz de usuario no se actualiza. Hay muchas otras cosas incorrectas en su código, lo arreglé todo, siéntase libre de preguntarme si no entiende algo:
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" /> </> ); };