Home > Software design >  How to modify variable in function call in functional React component
How to modify variable in function call in functional React component

Time:09-03

export default function TopNav() {
  const ns = 'header';
  const { t, i18n } = useTranslation(ns);
  const data = getData(i18n.language, ns);

  var topNav = [];

  const parseData = () => {
    data.then((json) => {

      const headerArray = mkNumArray(json, 'row1');
      var topNav = headerArray.map((i) => {
        const url = t("row"   i   ".button"   i   ".url");
        const label = t("row"   i   ".button"   i   ".label");
        return (
         <a href={url}>{label}</a>
       );
      })
      console.log(topNav);
    });
  };

  parseData(topNav);
  console.log(topNav);

  return(
     ....
  )
}

I'm having trouble figuring out how to handle this nav variable. Because of the async request in getData, I want to have the variable set inside this parseData function, but then want to have it available for returning in the component. The console log inside ParseData is correct, but the one before return is empty. What's the best way to do this?

There's some external functions defined here, but I don't think they are relevant.

CodePudding user response:

You should read useState and useEffect.

The common flow is like this: make topNav a state of your component. its initial value may be empty. after the first time rendering, the parseData is triggered and set value for topNav hence trigger re-rendering your component with new value of topNav.

Your code may resemble below:

export default function TopNav() {

  const [topNav, setTopNav] = useState([]);

  useEffect(()=>{

    const data = getData(i18n.language, ns);

    const parseData = () => {
     data.then((json) => {

      // ** do logic...
      
      setTopNav(/*value of topnav*/);
     });
    };
    
    parseData();

  }, []);

 
  return(
     ....
  )
}

CodePudding user response:

Instead of using a variable as topNap use something like a "state variable". I mean, use something like setState within:

...
const me = this;

const parseData = () => {
    data.then((json) => {

      const headerArray = mkNumArray(json, 'row1');
      var topNav = headerArray.map((i) => {
        const url = t("row"   i   ".button"   i   ".url");
        const label = t("row"   i   ".button"   i   ".label");
        return (
         <a href={url}>{label}</a>
       );
      })

      me.setState(stateTopNavVar, topNav);
      console.log(topNav);
    });
  };

Sometimes with react it is also needed to use the setState callback. Well, this will depend on if you are using React classes or not. Remember, variables as state variables are the means used by React to propagate changes on the React Virtual DOM

CodePudding user response:

Best way would be to modify getData to make it return a value, not a promise. Or you can create a custom hook that will do that for you. Like that is more "react" way. Next thing - do not declara variables and exeute functions in way you did. For states - useState or useMemo. For functions calls - useEffect.

Custom hook:

function useGetData(param1, param2) {
  // undefined = not loaded or error
  // null = empty response
  const [data, setData] = useState(undefined);
  const dataPromise = useMemo(() => {
    return getData(param1, param2);
  }, [param1, param2]);

  useEffect(() => {
    setData(undefined);
    if (!dataPromise) return;
    dataPromise
      .then((res) => setData(res || null))
      .catch((e) => {
        console.error(e);
        setData(undefined);
      });

    return () => setData(undefined);
  }, [dataPromise]);

  return data;
}

Usage:


function TopNav() {
  const ns = "header";
  const { t, i18n } = useTranslation(ns);
  const data = useGetData(i18n.language, ns);

  const topNav = useMemo(() => {
    if (!data) return [];

    const headerArray = mkNumArray(json, "row1");
    return headerArray.map((i) => {
      const url = t("row"   i   ".button"   i   ".url");
      const label = t("row"   i   ".button"   i   ".label");
      return <a href={url}>{label}</a>;
    });
  }, [data]);

  return <> what you had here </>;
}
  • Related