Search code examples
reactjstypescriptreact-hooksresponsive

How can I show/hide element depending on visibility of another element?


I am trying to fix a bug on the website I am building, on responsive version of it. There are two "bars" in the upper side of website - the upper one with major tabs, and the lower is with icons specific for chosen tab.

When I am changing screen size (as if to test I were using mobile), both tabs appear as small squares (as should).

Again, clicking on upper square will show a list of tabs, and clicking on lower one will show a list of icons. Clicking a second time, hides relevant list. The problem is, that if both are clicked, then both lists appear and positioned one upon another.

What I want to do, that if icon list is shown, then tab list should be hidden and vice-versa.

Now, both elements have "onClick" function that changes relevant variable\prop and show\hide itself. But how to "load" this value on another element?

How it is built: I have the "main" tsx file, which holds both tabs and icons elements.

In main file:

        <TabsMobile
          tab={tab}
          setTab={setTab}
          setPages={setPages}           
        /> 

 {<AdditionalMobile/>}

TabsMobile is for tabs, AdditionalMobile is for icons.

TabsMobile file:

   interface ITabsMobile {
      TabsArray: Array<Type>;
      tab: number;
      setTab: (value: number) => void;
      setPages?: any;
    }
    
    interface Type {
      page: number;
      title: string;
    }
    
    export const TabsMobile: FC<ITabsMobile> = ({
      TabsArray,
      tab,
      setTab,
      setPages
    }) => {
      const [open, setOpen] = useState(false);
    
      function updateOpen()
      {
          var openString : string = !open == true ? "true" : "false";      
          localStorage.setItem("mainTabOpen", openString);
          console.log('Inside: '+localStorage.getItem("mainTabOpen"));    
          setOpen(!open);
      }
    
      return (
        <div className={styles.wrapper}>
          <div className={styles.head} onClick={() => updateOpen()}>
            <p>Upper tabs</p>
          </div>
          {open && (
            <div className={styles.body}>
              {TabsArray.map((item: any, index) => (
                <TabsItem
                key={index}
                title={item.title}
                page={index}
                tab={tab}
                setTab={setTab}
                pages={index}
                setPages={setPages}
                />
              ))}
            </div>
          )}
        </div>
      );
    };

Now, as I understand to make AdditionalMobile hidden or invisible I need to do something like this in main file:

{isMainTabShown && <AdditionalMobile/>}

If "isMainTabShown" is true, then the element will be shown.

But, I for now, didn't find a way to:

  1. Transfer variable from "TabsMobile" to main file
  2. "Reload" this line of code: {isMainTabShown && <AdditionalMobile/>}

I tried two things, but didn't succeed. Help me with either of those, or if there is a better solution, please share it:

  1. Use "localStorage"

       var openString : string = !open == true ? "true" : "false";      
       localStorage.setItem("mainTabOpen", openString);
    

This does indeed updates the relevant variable, but how I can "reload" it in main file?

I tried using useEffect:

useEffect(() => {
     console.log('Outside: '+localStorage.getItem("mainTabOpen"));
 }, []);

and also like this:

useEffect(() => {
     console.log('Outside: '+localStorage.getItem("mainTabOpen"));
 }, [localStorage.getItem("mainTabOpen")]);

But the code doesn't even seem to enter those functions.

I also tried solution from here: useEffect does not listen for localStorage

useEffect(() => {
  function checkUserData() {
    const item = localStorage.getItem('userData')

    if (item) {
      setUserData(item)
    }
  }

  window.addEventListener('storage', checkUserData)

  return () => {
    window.removeEventListener('storage', checkUserData)
  }
}, [])

But again, it didn't seem to "catch" change of localStorage variable.

  1. I tried to export "open" variable:

     export const TabsMobile: FC<ITabsMobile> = ({
       //...
       openExternal,
       setOpenexternal
     }) => {
       const [open, setOpen] = useState(false);
    

But again, how to access it externally and make the "AdditionalMobile" appear/hide depending on it?


Solution

  • I managed to find a workaround. Maybe not too smart, but it does resolve the issue. I will post it here for others:

    The problem is, that something must be triggered in main file, so the new value of "mainTabOpen" item from "localStorage".

    What I did - in addition to "onClick" event inside the element - in element function, I also placed another outside of it in main file.

    In element file:

      function updateOpen()
      {
          var openString : string = !open == true ? "true" : "false";      
          localStorage.setItem("mainTabOpen", openString);
          setOpen(!open);
      }
    

    In main file:

            <div onClick={() =>  resetVisibility()}>
              <TabsMobile
                TabsArray={TabsTransactionsArray}
                tab={tab}
                setTab={setTab}
                setPages={setPages}           
              />   
            </div>  
    
      function resetVisibility()
      {
         setMainTabOpen(localStorage.getItem("mainTabOpen") == "true" ? true : false);
         console.log('Outside: '+mainTabOpen);
      }
    
    {!mainTabOpen && <AdditionalMobile/>} 
    

    I already have "useEffect" in main file, so I didn't have to add a new one, but if someone uses this approach, they might need it.

    Why I just don't change the value in main file? Because if I do this, the value will be changed not only when clicking on "show tabs" button, but also when changing a tab.

    For example (icons element appear when "show tabs is False)":

    • Starting value: false

    • I click on 'show tabs" to show it, value: true

    • I click on tab I select, value: false

    • I click on 'show tabs" to hide it, value: false

    As result "icons" element is hidden, while should be visible.

    Event "onClick" assigned to "div" surrounding the button will trigger not only on "show tabs" button is clicked, but also when buttons of specific tab (children of "show tabs") are used.

    Event "onClick" assigned to element, will trigger only when "show tabs" button is clicked.

    Because of it, the actual value of "mainTabOpen" must be changed when clicking on button "show tabs" itself, in the element function, and not in main file function.