Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Custom Hooks fetch data globally and share across components?

in this react example from https://reactjs.org/docs/hooks-custom.html, a custom hook is used in 2 different components to fetch online status of a user...

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

then its used in the 2 functions below:

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

my question is, will the function be executed individually everywhere where it is imported into a component? Or is there some thing like sharing the state between components, if it is defined as a separate exported function? e.g I execute the function only once, and the "isOnline" state is the same in all components?

And if its individually fetched, how would I have to do it to fetch data only once globally, and then pass it to different components in my React app?

like image 757
Marc_L Avatar asked Aug 22 '19 05:08

Marc_L


People also ask

How do you share data between components in React hooks?

First, you'll need to create two components, one parent and one child. Next, you'll import the child component in the parent component and return it. Then you'll create a function and a button to trigger that function. Also, you'll create a state using the useState Hook to manage the data.

Do custom hooks share state?

Do two components using the same Hook share state? No. Custom Hooks are a mechanism to reuse stateful logic (such as setting up a subscription and remembering the current value), but every time you use a custom Hook, all state and effects inside of it are fully isolated.

How do you fetch data using React hooks?

Put the fetchData function above in the useEffect hook and call it, like so: useEffect(() => { const url = "https://api.adviceslip.com/advice"; const fetchData = async () => { try { const response = await fetch(url); const json = await response. json(); console. log(json); } catch (error) { console.


1 Answers

To share state data across multiple components in a large project, I recommend to use Redux or React Context.

Nevertheless, you can implement a global isOnline state using the Observer pattern (https://en.wikipedia.org/wiki/Observer_pattern):

// file: isOnline.tsx
import { useEffect, useState } from 'react';

// use global variables
let isOnline = false;
let observers: React.Dispatch<React.SetStateAction<boolean>>[] = [];

// changes global isOnline state and updates all observers
export const setIsOnline = (online: boolean) => {
    isOnline = online;
    observers.forEach(update => update(isOnline));
};

// React Hook
export const useIsOnline = (): [boolean, Function] => {
    const [isOnlineState, setIsOnlineState] = useState<Object>(isOnline);

    useEffect(() => {
        // add setIsOnlineState to observers list
        observers.push(setIsOnlineState);

        // update isOnlineState with latest global isOnline state
        setIsOnlineState(isOnline);

        // remove this setIsOnlineState from observers, when component unmounts
        return () => {
            observers = observers.filter(update => update !== setIsOnlineState);
        };
    }, []);

    // return global isOnline state and setter function
    return [isOnlineState, setIsOnline];
}
import { useIsOnline } from './isOnline';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useIsOnline();

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

function FriendStatus(props) {
  const isOnline = useIsOnline()[0];

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useIsOnline()[0];

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

Edit: Inside useIsOnline return isOnlineState created with useState instead of the isOnline variable because otherwise React can't pass the changes down to the component.

Edit 2: Make useEffect independent of isOnlineState so the hook does not unsubscribe and resubscribe on each variable change.

like image 186
neumann Avatar answered Oct 11 '22 03:10

neumann