0Interest
0Interest

Reputation: 1842

Filtering data and returning it next to the "permanent" data - react

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)

Picture of the input

The buttons do not show unfortunately...

Upvotes: 2

Views: 105

Answers (2)

DilipCoder
DilipCoder

Reputation: 578

    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"
          />
        </>
      );
    };

Upvotes: 1

NearHuscarl
NearHuscarl

Reputation: 81803

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"
      />
    </>
  );
};

Live Demo

Codesandbox Demo

Upvotes: 1

Related Questions