reactjsreact-hooksreact-componentuse-contextdata-sharing

How can i share data between non-related components in react js?


I'm quite new with reactjs, and I'm struggling with the following: I have a Header component (in my App.js file) which contains an AudioPlayer. This AudioPlayer needs to be updated with current value of src (with the path of the song, stored in local) and title. Now, think about something like this:

App.js

function App(){
    <Header/>
    <Page1/>
    <Page2/>
    <Page3/>
    ...
    <SomePage/>
    ...
    <Footer/>
}

Header.js

import HeaderLogic from './HeaderLogic'

funtion Header(){

    const {property1, property2, ..., path, title} = HeaderLogic()
    //In HeaderLogic I want to get path and title, set in SomePageLogic.js
    ...
    return <>
        <AudioPlayer
            src={path}
            title={title}
        />
        ...
    </>
}

SomePage.js

import SomePageLogic from './SomePageLogic'
import songPath from './somePath/songPath'

function SomePage(){
    const title = 'songTitle'
    const {property1, property2, ..., propertyN} = SomePageLogic(songPath, title)
    //In SomePageLogic (or maybe here) I want to set path and title of the song,
    //in order to be used from the HeaderLogic component

    return <>
        ...
    </> 
}

I'm not sure useContext could be fine in this case, since I want to share data between unrelated components... Of course, the Header should rerender when it detects the song has been set. Should I use some kind of subscription pattern? Any hint?

Thank you very much!


Solution

  • No need for any subscription pattern and definitely not using localStorage - context is exactly used for your usecase: sharing state between unrelated components.

    So for instance, I want to share selected menu between the header and the Page1 somewhere below, you'd create a context with createContext(), export it's provider, wrap the children, and then you share that state between all components you set as children. Example:

    const defaultSelectedMenu = 'a';
    export const MenuContext = createContext({ selectedMenu: defaultSelectedMenu });
    export const MenuProvider = ({ children }) => {
      const [selectedMenu, setSelectedMenu] = useState(defaultSelectedMenu);
      return (<MenuContext.Provider value={selectedMenu, setSelectedMenu}>
          {children}</MenuContext.Provider>)
    }
    

    And then in your app.js:

    ...
    <MenuProvider>
      <Header />
      <Page1 />
    </MenuProvider>
    <Page2 />
    ...
    

    Now, your Header and Page1 components can access the MenuContext through useContext(MenuContext) and have access to the state and the mutator.

    This was just a simple example but the same logic applies to any logic you want to share, heavy computation, API calls, you name it. You define the implementation in the <Provider> and then consume the implementation by accessing it through useContext