skip to Main Content

I have a custom hook written to show or hide a list when a button clicked. Below is the custom hook snippet I have written:

import { useEffect } from "react";
import { useState } from "react";

function useVisibilityStatus() {

    const [isVisible, setIsVisible] = useState(true);

    useEffect( () => {

        setIsVisible(!isVisible);              


    }, [isVisible]);

    return isVisible;
}

export default useVisibilityStatus;

And in another component I have written the logic to view or hide the list.

import ProductListView from "../common/ProductListView";
import SideNavView from "../common/SideNavView";
import SimpleMap from '../googlemap/SimpleMap';
import useVisibilityStatus from '../customhooks/useVisibilityStatus';

function ServiceContentView() {
    const listVisible = useVisibilityStatus();

    return (
        <div className="row p-0 vw-100 service-view-body bg-primary position-relative">
            <div className="zindex-value position-absolute">
              <div className="service-view-sidenav float-end">
                <SideNavView />
              </div>
            </div>
            {listVisible && <ProductListView />}
            <SimpleMap />
        </div>
    );
}

export default ServiceContentView;

And in the SideNavView I have the button where upon clicking it shows the product list or hides it if it is visible. The SideNavView is incomplete, as I am bit struggling on how to fit my requirement in ReactJS.

import useVisibilityStatus from '../customhooks/useVisibilityStatus';

function SideNavView() {
    const   listVisible = useVisibilityStatus();

    const handleClick = () => {
        
    }

    return (
        <div className="">
            <div className="row">                
                <div className="card p-0 w-100 border-radius-none sidenav-view" onClick={handleClick}>
                    <span className="text-center mt-2 text-white">
                        <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="currentColor" className="bi bi-card-list" viewBox="0 0 16 16">
                            <path d="M14.5 3a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h13zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13z"/>
                            <path d="M5 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 5 8zm0-2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-1-5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zM4 8a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0zm0 2.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z"/>
                        </svg>
                    </span>
                    <div className="card-body p-0">
                        <h5 className="card-title text-center text-white">View Products</h5>
                    </div>
                </div>                
            </div>
        </div>
    )
}

export default SideNavView;

3

Answers


  1. React hooks don’t magically share state, so with the current useVisibilityStatus implementation they will each have and update their own state independently.

    What you can do is create a React Context to hold the single isVisible state value and expose it and an updater function to consumers.

    import { createContext, useContext, useState } from 'react';
    
    const VisibleContext = createContext({
      isVisible: false,
      toggleIsVisible: () => {},
    });
    
    export const useVisibilityStatus = () => useContext(VisibleContext);
    
    const VisibleContextProvider = ({ children }) => {
      const [isVisible, setIsVisible] = useState(true);
    
      const toggleIsVisible = () => setIsVisible(isVisible => !isVisible);
    
      return (
        <VisibleContext.Provider value={{ isVisible, toggleIsVisible }}>
          {children}
        </VisibleContext.Provider>
      );
    };
    
    export default VisibleContextProvider;
    

    The VisibleContextProvider just needs to wrap the part of the ReactTree that contains the components that need to access its context value, e.g. ServiceContentView and SideNavView, and these components use the useVisibilityStatus to read the current isVisible state value or access the toggleIsVisible updater function.

    import VisibleContextProvider from '../path/to/VisibleContextProvider';
    
    ...
    
    return (
      ...
      <VisibleContextProvider>
    
        ...
        <ServiceContentView />
        ...
    
      </VisibleContextProvider>
      ...
    );
    
    import ProductListView from "../common/ProductListView";
    import SideNavView from "../common/SideNavView";
    import SimpleMap from '../googlemap/SimpleMap';
    import { useVisibilityStatus } from '../path/to/VisibleContextProvider';
    
    function ServiceContentView() {
      const { isVisible } = useVisibilityStatus();
    
      return (
        <div className="row p-0 vw-100 service-view-body bg-primary position-relative">           
          <div className="zindex-value position-absolute">
            <div className="service-view-sidenav float-end">
              <SideNavView />
            </div>
          </div>
          {isVisible && <ProductListView />}
          <SimpleMap />          
        </div>
      );
    }
    
    export default ServiceContentView;
    
    import { useVisibilityStatus } from '../path/to/VisibleContextProvider';
    
    function SideNavView() {
      const { toggleIsVisible } = useVisibilityStatus();
    
      const handleClick = () => {
        toggleIsVisible();
      };
    
      return (
        <div className="">
          <div className="row">                
            <div
              className="...."
              onClick={handleClick}
            >
              <span className="text-center mt-2 text-white">
                ...
                </svg>
              </span>
              <div className="card-body p-0">
                <h5 className="....">View Products</h5>
              </div>
            </div>                
          </div>
        </div>
      )
    }
    
    export default SideNavView;
    
    Login or Signup to reply.
  2. You don’t need to use custom hooks for that task. The better solution is to save visibility status into Main component’s container and pass it as props to child components.

    Login or Signup to reply.
  3. To share state between deeply nested component you can use context

    VisibilityContext.jsx

    import { createContext, useState } from 'react'
    
    const VisibilityContext = createContext({
      Visibility: {}
    })
    
    export const VisibilityContextProvider = ({ children })=>{
      const [Visibility, SetVisibility] = useState({})
    
      return (
        <VisibilityContext.Provider value={{ Visibility, SetVisibility }}>
          {children}
        </VisibilityContext.Provider>
      )
    }
    
    export default VisibilityContext
    

    useVisibilityStatus.js

    import { useState, useContext, useCallback } from "react";
    
    import VisibilityContext from 'path/to/VisibilityContext.jsx'
    
    const useVisibilityStatus = (scope) => {
      const { Visibility, SetVisibility } = useContext(VisibilityContext)
      
      const ToggleVisibility = useCallback(() => {
        SetVisibility(prev => !prev)
      }, [SetVisibility])
    
      return {
        IsVisible: Visibility[scope] ?? false,
        ToggleVisibility
      }
    }
    
    export default useVisibilityStatus;
    

    You need to wrap the SideNavView and ServiceContentView with VisibilityContextProvider

    SideNavView and ServiceContentView Parent

    import { VisibilityContextProvider } from 'path/to/VisibilityContext.jsx'
    
    const ... = () => {
      return (
        <VisibilityContextProvider>
           {/* SideNavView and ServiceContentView */}
        </VisibilityContextProvider>
      )
    }
    

    ServiceContentView.jsx

    import useVisibilityStatus from 'path/to/useVisibilityStatus.js'
    
    const ServiceContentView = () => {
      const { IsVisible } = useVisibilityStatus('ServiceContentView')
    
      return (
        ...
        {IsVisible && <ProductListView />}
        ...
      )
    }
    

    SideNavView.jsx

    import useVisibilityStatus from 'path/to/useVisibilityStatus.js'
    
    const SideNavView = () => {
      const { ToggleVisibility } = useVisibilityStatus('ServiceContentView')
    
      const handleClick = () => {
        ToggleVisibility()
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search