1

I am a beginner in React, I have a question regarding rendering, which consists of I use useEfect to render the change in a variable, which is decorated in a useState

Here are the statements:

const CardRepo: React.FC<DadosCardRepo> = ({ Nome }) => {

const [nomeRepo, setNomeRepo] = useState(Nome);
const [data, setData] = useState({});
const [languages, setLanguages] = useState<Languages[]>([]);

This is a component, which receives an object with a Name, and sets this value Name in nameRepo

Here are the two methods:

useEffect(() => {
        getRepo(nomeRepo)
            .then(data => {
                setData(data);
            });
}, [nomeRepo]);

useEffect(() => {
        getLanguages(data['languages_url'])
            .then(data => {
                let total = 0;
                const languagesRef: Languages[] = [];
                Object.keys(data).map((key) => {
                    total += data[key];

                    languagesRef.push({ Nome: key, Porct: data[key] });
                });

                languagesRef.map((language, i, array) => {
                    setLanguages((oldValues) => [...oldValues, { Nome: language.Nome.toLowerCase(), Porct: +((language.Porct * 100) / total).toFixed(2) }]);
                });
            });
}, [data]);

Where the first useEfect I run when making a change to nomeRepo, and in this method, I make a request to the getRepo service

And the second one, when I change the date, and in this method, I make a request to the getLanguages ​​service, and do the processing of the response to make a list with it, where I assign languages.

Here is the listing:

{
  languages.map((el, i) => {
      return (
          <div className="progress-bar" id={el.Nome} key={i} style={{ width: el.Porct + '%' }}></div>
      );
  })
}

My question is related to rendering, I know that I will only be able to list something, if I use useEfect in the variable "languages", where I will be assigning values ​​and with the changes in the variable to render again, otherwise I couldn't.

But when I make any changes to the code, it renders only where it was changed, but it assigns the same values ​​that were assigned, for example: [1,2,3] => [1,2,3,1,2,3]. I tried to put setLanguages ​​([]), but it has the same behavior I was wondering how to fix this and if it's a good practice to make calls and list that way.

1 Answer 1

1

first, there is one bad practice where you create a derived state from props. nomeRepo comes from props Nome, you should avoid this derived states the most of you can. You better remove nomeRepo state and use only Nome.

your first use Effect becomes:

useEffect(() => {
        getRepo(Nome)
            .then(data => {
                setData(data);
            });
}, [Nome]);

you second useEffect has a setState triggered inside a map. That will trigger your setState multiple times, creating several rerenders. It's best to prepare your array of languages first, then call setState once.

Also, you should use forEach instead of map if you are not consuming the returned array, and only running some script on each element.

One thing to notice, is you are adding new languages to old ones. I assume that languages should be related data['languages_url'] value, this way you wouldn't include the old languages to the setLanguages.

One thing you could consider is to keep your total values instead of percentages. you can store total into a variable with useMemo, that will depend on languages. And declare a your percentage function into a utils folder and import it as need it.

refactored second useEffect:

// calculate total value that gets memoized
const totalLangCount = useMemo(() => languages.reduce((total, val) => total + val.count , 0), [languages])

useEffect(() => {
  getLanguages(data['languages_url'])
      .then(data => {
          const languagesRef: Languages[] = [];
          Object.keys(data).forEach((key) => {
              // prepeare your languages properties
              const nome = key.toLowerCase();
              const count = data[key];

              languagesRef.push({ nome, count });
          });

          // avoid multiple setLanguages calls
          setLanguages(languagesRef)
      });
}, [data]);

languages rendered:

{
  languages.map((el, i) => {
      return (
          //  here we are using array's index as key, but it's best to use some unique key overall instead
          <div className="progress-bar" id={el.nome} key={i} style={{ width: percentage(el.count, total) }}></div>
      );
  })
}

percentage function reusable:

// utils percentage exported
export const percentage = (count, total) => ((count * 100) / total).toFixed(2) + '%'
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your answer and help, it helped me a lot, I made all the changes you said and everything went well, in addition to learning

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.